리눅스 명령어

chmod 755와 644의 실제 차이, 왜 폴더는 755여야 하나

chmod 755와 644의 실제 차이, 왜 폴더는 755여야 하나

웹호스팅에 워드프레스를 올리다 보면 꼭 이런 안내를 만납니다. “파일은 644, 폴더는 755로 맞춰주세요.” 플러그인 설치가 안 될 때도, 이미지 업로드가 실패할 때도 같은 문장이 나옵니다. 그래서 일단 시키는 대로 chmod 755를 쳐 보고 되면 넘어갑니다.

문제는 그다음입니다. 어떤 날은 755로 고쳐도 여전히 403 Forbidden이 뜹니다. 어떤 날은 파일 권한은 멀쩡한데 쓰기가 안 됩니다. 이유를 모르면 매번 감으로 숫자를 바꿔가며 찍게 됩니다. 이 숫자가 실제로 무엇을 허용하는지, 특히 디렉터리에서 실행 권한 x가 왜 필요한지만 이해하면 그 감이 판단으로 바뀝니다.

한눈에 · 숫자 읽는 법(4/2/1) → 내 파일 현재 권한 확인(ls -ld, stat) → 디렉터리 x의 의미 실험 → find로 파일/폴더 나눠 적용 → 403·Permission denied 갈림길 진단

755, 644라는 숫자는 어디서 나온 건가

리눅스 권한은 세 부류의 사용자에게 각각 따로 붙습니다. 파일 주인(소유자), 그 파일이 속한 그룹, 그리고 나머지 전부(기타)입니다. 755가 세 자리인 이유가 여기 있습니다. 앞에서부터 소유자·그룹·기타 순서입니다.

각 자리의 숫자는 세 가지 권한을 더한 값입니다. 읽기가 4, 쓰기가 2, 실행이 1입니다. 이 세 숫자는 겹치지 않게 골라져 있어서, 합계만 보면 어떤 조합인지 유일하게 역산됩니다.

읽기 4, 쓰기 2, 실행 1이 더해지는 방식

숫자조합기호뜻
74+2+1rwx읽기·쓰기·실행 전부
64+2rw-읽고 쓰기, 실행 불가
54+1r-x읽고 실행, 수정 불가
44r–읽기만
00—아무것도 안 됨

이 표를 세 자리에 그대로 대입하면 755와 644가 무슨 뜻인지 바로 나옵니다.

숫자기호소유자그룹기타
755rwxr-xr-x읽기·쓰기·실행읽기·실행읽기·실행
644rw-r–r–읽기·쓰기읽기읽기
700rwx——읽기·쓰기·실행없음없음
600rw——-읽기·쓰기없음없음

두 값의 차이는 딱 하나, 실행 권한 x가 붙어 있느냐뿐입니다. 755에서 x를 전부 빼면 644가 됩니다(정확히는 644 + 실행 = 755). 그러니 “폴더는 왜 755인가”라는 질문은 사실 “폴더에는 왜 x가 필요한가”라는 질문입니다.

ls -l 출력의 첫 열 읽는 법

ls -l을 치면 맨 앞에 열 글자짜리 문자열이 나옵니다.

$ ls -l
drwxr-xr-x 2 ubuntu ubuntu 4096 Mar  3 10:12 uploads
-rw-r--r-- 1 ubuntu ubuntu  2451 Mar  3 10:12 index.php

맨 앞 한 글자는 종류입니다. d면 디렉터리, -면 일반 파일, l이면 심볼릭 링크입니다. 그 뒤 아홉 글자를 셋씩 끊으면 소유자·그룹·기타입니다. 위 출력에서 uploads는 rwx r-x r-x, 즉 755입니다. index.php는 rw- r-- r--, 644입니다.

실습 전에 확인할 것: 지금 내 파일 권한 보기

숫자 읽는 법을 잡았으면, 이제 자기 서버의 실제 상태를 봐야 합니다. 고치기 전에 현재 값을 알아야 무엇이 바뀌었는지 알 수 있습니다.

문자열을 매번 세는 게 번거로우면 stat으로 숫자를 바로 뽑으면 됩니다.

$ stat -c '%a %n' index.php
644 index.php

$ stat -c '%a %n' *
755 uploads
644 index.php
644 wp-config.php

%a가 권한 숫자, %n이 파일 이름입니다. 여러 파일을 한 번에 보고 싶을 때 ls -l보다 눈이 덜 아픕니다.

ls -ld로 디렉터리 자체를 보는 이유

여기서 초보자가 자주 헛다리를 짚습니다. 디렉터리 이름을 ls -l에 넘기면 그 디렉터리 안의 내용물이 나옵니다. 정작 그 디렉터리 자신의 권한은 안 보입니다.

$ ls -l uploads
합계 12
-rw-r--r-- 1 ubuntu ubuntu 3021 Mar  3 10:20 photo.jpg

$ ls -ld uploads
drwxr-xr-x 2 ubuntu ubuntu 4096 Mar  3 10:20 uploads

-d 옵션이 “내용 말고 이 항목 자체를 보여줘”라는 뜻입니다. 403이나 Permission denied를 추적할 때는 거의 항상 ls -ld 쪽이 필요합니다.

chmod와 chown을 헷갈리지 않기

한 줄로 정리하면 이렇습니다. chmod는 “무엇을 할 수 있는가”를 바꾸고, chown은 “누구의 것인가”를 바꿉니다. 644라는 권한은 “소유자는 쓸 수 있다”만 말할 뿐, 그 소유자가 누구인지는 말하지 않습니다.

그래서 웹서버가 파일을 못 쓰는 상황이 두 갈래로 나뉩니다. 권한 숫자가 부족한 경우와, 숫자는 맞는데 소유자가 웹서버 계정이 아닌 경우입니다. 뒤쪽은 chmod를 아무리 만져도 안 풀립니다.

그리고 자기 소유가 아닌 파일의 권한은 일반 계정으로 못 바꿉니다.

$ chmod 644 /etc/hosts
chmod: changing permissions of '/etc/hosts': Operation not permitted

이 메시지가 뜨면 권한 조합이 틀린 게 아니라 내가 그 파일 주인이 아니라는 뜻입니다. sudo가 필요한지, 애초에 건드리면 안 되는 파일인지부터 판단하세요.

디렉터리의 x는 ‘실행’이 아니라 ‘통과’다

앞에서 755와 644의 차이가 x 하나라고 했습니다. 이 x가 파일과 디렉터리에서 전혀 다른 의미라는 게 이 글의 핵심입니다.

일반 파일에서 x는 말 그대로 실행입니다. 쉘 스크립트나 바이너리를 ./script.sh로 돌릴 수 있게 해 줍니다. 그런데 디렉터리는 실행할 수 있는 물건이 아닙니다. 디렉터리에서 x는 “이 디렉터리 안으로 들어가거나, 경로의 일부로 통과할 수 있다”는 의미로 쓰입니다. 그래서 x를 search permission이라고 부르기도 합니다.

디렉터리를 644로 만들면 벌어지는 일

말로만 하면 안 와닿으니 직접 재현해 보는 게 빠릅니다. 실제 서비스 디렉터리 말고 테스트용으로 하나 만들어서 해 보세요.

$ mkdir -p /tmp/permtest/inner
$ echo "hello" > /tmp/permtest/inner/a.txt
$ chmod 644 /tmp/permtest/inner
$ cd /tmp/permtest/inner
-bash: cd: inner: Permission denied

cd부터 막힙니다. x가 없으면 그 디렉터리를 현재 위치로 삼을 수 없습니다. 이미 그 안에 들어가 있는 상태에서 권한을 뺐다면 ls도 이렇게 나옵니다.

$ ls -l
ls: cannot open directory '.': Permission denied

반대로 r만 빼고 x는 남긴 경우(예: 711)는 또 다릅니다. 목록은 못 보지만, 정확한 파일 이름을 알고 있으면 cat /tmp/permtest/inner/a.txt처럼 직접 접근은 됩니다. r은 “이름 목록을 읽는 권한”, x는 “통과하는 권한”으로 역할이 갈립니다.

디렉터리 권한ls로 목록 보기cd로 들어가기안쪽 파일 직접 열기
r-x (5)가능가능가능
r– (4)이름만 보임불가불가
–x (1)불가가능이름 알면 가능

정리하면 이렇습니다. 디렉터리는 들어가야 쓸모가 있으니 x가 필수고, 그래서 기본값이 755입니다. 일반 파일은 실행할 일이 없으니 x를 뺀 644가 기본값입니다.

디렉터리에서 x를 빼면
디렉터리에서 x를 빼면

실행 파일과 쉘 스크립트는 왜 예외인가

일반 파일 중에도 x가 필요한 게 있습니다. 배포 스크립트나 크론에서 돌리는 .sh 파일입니다. 이런 건 644로 두면 Permission denied가 나며 실행이 안 됩니다.

$ chmod u+x deploy.sh
$ ./deploy.sh

u+x는 “소유자에게만 실행 권한 추가”입니다. 스크립트를 나만 돌릴 거면 굳이 755까지 안 올려도 됩니다. 참고로 PHP 파일은 웹서버가 읽어서 해석하는 것이지 OS가 실행하는 게 아닙니다. .php에 x를 줄 필요는 없습니다.

755와 644를 나눠서 한 번에 적용하기

원리를 알았으니 이제 디렉터리 전체에 적용할 차례입니다. 여기서 가장 흔한 실수가 chmod -R 755 .입니다.

-R은 하위 전체에 재귀 적용이라는 뜻인데, 파일과 디렉터리를 구분하지 않습니다. 그러면 index.php, wp-config.php, 이미지 파일까지 전부 rwxr-xr-x가 됩니다. 동작은 하지만 실행할 필요 없는 파일에 실행 권한이 붙은 상태입니다. 권한은 필요한 만큼만 주는 게 원칙이고, 이걸 어기면 나중에 무엇이 정상인지 판단할 기준이 사라집니다.

find로 파일과 디렉터리를 나눠 처리

그래서 find로 종류를 나눠서 거는 방식이 표준처럼 쓰입니다. 웹 루트로 이동한 뒤 두 줄을 순서대로 실행합니다.

$ cd /var/www/html
$ find . -type d -exec chmod 755 {} \;
$ find . -type f -exec chmod 644 {} \;

-type d가 디렉터리, -type f가 일반 파일입니다. {}에 찾은 항목 이름이 들어가고, \;가 명령의 끝을 표시합니다. 파일이 많으면 -exec chmod 755 {} + 형태가 훨씬 빠릅니다. 여러 개를 한 번에 묶어서 처리하기 때문입니다.

실행 전에 어떤 게 걸리는지 확인하고 싶으면 -exec 없이 먼저 돌려 보세요.

$ find . -type d | head
$ find . -type f -perm 755

두 번째 명령은 적용 후 검사용으로도 씁니다. 일반 파일인데 755로 남아 있는 게 있다면 출력됩니다. 아무것도 안 나오면 깔끔하게 정리된 겁니다.

웹 디렉터리 권한 정리 순서
웹 디렉터리 권한 정리 순서

대문자 X 옵션이 편한 경우

find를 두 번 치기 번거로우면 기호 방식에 대문자 X를 쓰는 방법이 있습니다.

$ chmod -R u+rwX,go+rX,go-w .

소문자 x는 무조건 실행 권한을 붙입니다. 대문자 X는 조건부입니다. 대상이 디렉터리이거나, 이미 누군가에게 실행 권한이 있는 파일일 때만 x를 붙입니다. 그래서 디렉터리에는 x가 들어가고 평범한 텍스트 파일은 건드리지 않습니다.

다만 이미 chmod -R 755를 한 번 걸어버린 뒤라면 모든 파일에 x가 있는 상태라, X를 써도 그대로 유지됩니다. 이런 경우엔 find 방식으로 확실히 초기화하는 게 낫습니다.

자주 막히는 지점: 403, Permission denied, 그리고 상위 디렉터리

여기까지 했는데도 브라우저에서 403 Forbidden이 사라지지 않는 경우가 있습니다. 이때 대부분은 파일 자체가 아니라 거기까지 가는 경로 중 어딘가에 x가 없어서 생깁니다.

웹서버가 /var/www/html/index.php를 읽으려면 /, /var, /var/www, /var/www/html을 차례로 통과해야 합니다. 중간 한 곳이라도 x가 없으면 최종 파일이 644든 777이든 못 갑니다. 사용자 홈 디렉터리 아래에 사이트를 두는 호스팅에서 특히 자주 터집니다. 홈 디렉터리를 chmod 700으로 잠그면 웹서버가 통과를 못 합니다.

namei -l로 경로 전체 권한 훑기

경로를 한 칸씩 ls -ld 하는 대신 namei를 쓰면 한 번에 나옵니다.

$ namei -l /var/www/html/index.php
f: /var/www/html/index.php
 drwxr-xr-x root     root     /
 drwxr-xr-x root     root     var
 drwxr-xr-x root     root     www
 drwxr-xr-x www-data www-data html
 -rw-r--r-- www-data www-data index.php

위에서부터 훑으면서 디렉터리 줄에 x가 빠진 곳이 있는지 봅니다. 하나라도 drw-r--r--처럼 x 없는 줄이 있으면 그게 범인입니다. namei가 없으면 apt install util-linux 또는 배포판 패키지로 들어 있는 경우가 많습니다.

권한 문제가 아닐 때

namei 출력이 전부 정상인데도 업로드가 안 되면 소유자를 봐야 합니다. 위 출력에서 세 번째, 네 번째 열이 소유자와 그룹입니다. 웹서버 프로세스가 www-data로 도는데 업로드 폴더 소유자가 ubuntu이고 권한이 755라면, 기타(others)에는 쓰기 권한이 없으니 씁니다가 실패합니다.

$ sudo chown -R www-data:www-data /var/www/html/wp-content/uploads

여기서 777로 열어 문제를 덮는 방법이 돌아다니는데, 권장하지 않습니다. 소유자나 그룹을 맞추는 게 정답이고, 777은 같은 서버의 다른 계정에게도 쓰기를 열어주는 조치입니다.

wp-config.php를 644 대신 640이나 600으로 낮추라는 조언도 자주 보입니다. DB 비밀번호가 들어 있으니 기타 사용자에게 읽기를 열지 말자는 취지입니다. 다만 이건 호스팅 구성에 따라 결과가 갈립니다. PHP가 사용자 계정 권한으로 도는지(suexec, php-fpm 풀 분리), 웹서버 계정과 파일 소유자가 같은 그룹인지에 따라 600으로 낮추면 사이트가 아예 안 뜰 수도 있습니다. 바꿔 보고 페이지가 정상인지 확인한 뒤 유지하세요.

그리고 CentOS/RHEL 계열에서는 SELinux가, 새로 만든 파일 권한이 예상과 다를 때는 umask 기본값이 얽혀 있을 수 있습니다. 이 둘은 chmod 바깥의 이야기라 여기서는 이름만 짚어 둡니다. 권한 이슈로 SSH 접속 자체가 막힌 상황이라면 Permission denied (publickey) 원인별 진단 순서 정리 쪽이 더 맞는 글입니다.

다음에 할 일 체크리스트

숫자를 외우기보다 “폴더는 들어가야 하니 x가 필요하다”는 이유 하나만 기억하면 나머지는 따라옵니다. 아래 순서로 자기 서버를 한 번 훑어 보세요.

  • [ ] stat -c '%a %n' *로 지금 웹 루트의 권한 상태를 눈으로 확인한다
  • [ ] find . -type f -perm 755를 돌려서 불필요하게 실행 권한이 붙은 파일이 있는지 본다
  • [ ] 있다면 find . -type d -exec chmod 755 {} \; / find . -type f -exec chmod 644 {} \;로 나눠서 정리한다
  • [ ] 실행이 꼭 필요한 스크립트만 chmod u+x로 되돌린다
  • [ ] 403이 뜬다면 namei -l /경로/파일로 상위 디렉터리 x부터 확인한다
  • [ ] 권한이 맞는데 쓰기가 안 되면 chown으로 소유자·그룹을 점검한다
  • [ ] ~/.ssh는 700, 개인키는 600으로 되어 있는지 ls -ld ~/.ssh와 ls -l ~/.ssh로 본다

마지막 항목이 중요합니다. SSH는 권한이 느슨하면 아예 키를 무시합니다. ~/.ssh가 700이 아니거나 개인키가 600보다 열려 있으면 접속이 거부됩니다. 이 부분은 SSH 키 인증 설정하는 법, 접속 거부까지 잡는 실전 순서에서 실제 설정 순서와 함께 다룹니다.

여기서 더 들어가고 싶다면 세 가지가 이어집니다. 새로 만드는 파일의 기본 권한을 정하는 umask, 그룹 단위로 협업 권한을 나누는 chown과 그룹 설계, 그리고 하위에 생성되는 파일의 그룹을 자동으로 상속시키는 setgid 비트입니다. 여러 명이 같은 웹 디렉터리를 만지는 환경이라면 setgid 쪽이 특히 쓸모 있습니다.

댓글 3개

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다