Nginx로 정적 파일 제공: 최적화 팁
적절한 캐시 헤더, Gzip 압축, 안전한 기본 설정, 숨김 파일 보호 팁을 통해 Nginx 정적 파일 제공을 최적화하여 더 빠른 사이트와 오래된 에셋 문제를 줄이세요.
Nginx로 정적 파일 제공: 최적화 팁
Nginx로 정적 파일을 제공하는 것은 이미지, CSS, JavaScript, 다운로드, 빌드된 프론트엔드 에셋을 전달하는 가장 일반적이고 효율적인 방법 중 하나입니다. Nginx는 이 작업에 매우 능숙하지만, 몇 가지 설정 선택에 따라 빠르고 예측 가능한 사이트와 대역폭을 낭비하거나 오래된 콘텐츠를 제공하는 사이트가 갈릴 수 있습니다.
정적 파일 최적화는 주로 명확한 경로, 올바른 캐싱 헤더, 압축, 안전한 기본 설정에 관한 것입니다. 강력한 결과를 얻기 위해 복잡한 설정이 필요하지는 않지만, 캐시 규칙이 파일 이름 지정 및 배포 방식과 일치해야 합니다.
명확한 정적 파일 위치로 시작하기
가장 간단한 정적 파일 설정은 root와 try_files를 사용합니다:
server {
listen 80;
server_name example.com;
root /var/www/example.com/public;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
이 구성으로 /css/app.css에 대한 요청은 /var/www/example.com/public/css/app.css로 매핑됩니다. 파일이 존재하지 않으면 Nginx는 404를 반환합니다.
이 직접 매핑은 디버깅할 때 유용합니다. 브라우저에서 URL을 가져와 파일 시스템 경로로 변환한 후 파일이 존재하는지 확인할 수 있습니다:
ls -l /var/www/example.com/public/css/app.css
파일이 존재하지만 Nginx가 404를 반환하면 다른 root, 더 구체적인 location 블록, 또는 경로를 재정의하는 include 파일을 찾아보세요.
단일 페이지 앱의 경우 알 수 없는 경로가 index.html로 폴백되도록 할 수 있습니다:
location / {
try_files $uri $uri/ /index.html;
}
이는 프론트엔드 라우터에 유용하지만 모든 사이트에 무분별하게 사용하지 마세요. 누락된 에셋도 index.html을 반환하면 JavaScript나 이미지 경로 디버깅이 혼란스러워질 수 있습니다. 많은 팀이 에셋에 대해 별도의 location을 사용하여 누락된 파일이 실제 404를 반환하도록 합니다.
URL 경로가 다른 파일 시스템 경로에 매핑되어야 할 때 alias를 사용할 수도 있습니다:
location /assets/ {
alias /srv/shared-assets/;
}
후행 슬래시에 주의하세요. alias를 사용할 때 location 경로와 파일 시스템 경로는 일반적으로 둘 다 /로 끝나야 합니다. 불일치는 예상치 못한 파일 경로를 생성할 수 있습니다.
안전한 패턴 중 하나는:
location /downloads/ {
alias /srv/downloads/;
try_files $uri =404;
}
여기서 /downloads/manual.pdf는 /srv/downloads/manual.pdf로 매핑됩니다. 후행 슬래시 규율 없이는 존재하지 않는 경로를 실수로 만들거나 게시하려는 의도가 없는 디렉토리를 노출하기 쉽습니다.
매칭 동작에 대한 자세한 내용은 Nginx location 블록을 참조하세요.
브라우저 캐싱 헤더 추가하기
정적 파일은 브라우저 캐싱에 탁월한 후보입니다. 사용자가 app.css를 한 번 다운로드하면 변경되지 않는 한 모든 페이지 조회에서 다시 가져오지 않아야 합니다.
버전 관리된 에셋의 경우 긴 캐시 수명을 사용하세요:
location /assets/ {
root /var/www/example.com/public;
expires 1y;
add_header Cache-Control "public, immutable";
}
이 방법은 배포 중에 파일 이름이 변경될 때 가장 잘 작동합니다(예: app.8f3a91.css 또는 bundle.20260523.js). 콘텐츠가 변경될 때 파일 이름이 변경되면 브라우저는 오래된 파일을 오랫동안 안전하게 캐시할 수 있습니다.
동일한 이름을 유지하는 파일의 경우 더 짧은 캐싱을 사용하세요:
location = /index.html {
root /var/www/example.com/public;
expires -1;
add_header Cache-Control "no-cache";
}
이 패턴은 프론트엔드 앱에서 일반적입니다. HTML 파일은 신선하게 유지되고, 해시된 CSS 및 JavaScript 파일은 적극적으로 캐시됩니다.
실용적인 예: React 또는 Vue 빌드는 해시된 에셋을 /assets/에 출력하고 일반 index.html을 출력합니다. /assets/는 1년 동안 캐시하지만 index.html은 재검증하도록 만드세요. 사용자는 빠른 재방문을 경험하고, 새 배포는 여전히 최신 에셋 참조를 로드합니다.
캐시 규칙을 변경한 후에는 추측하지 말고 헤더를 테스트하세요:
curl -I https://example.com/assets/app.8f3a91.css
curl -I https://example.com/
해시된 에셋은 긴 수명의 Cache-Control 값을 표시해야 합니다. HTML 페이지는 일반적으로 재검증하거나 짧은 수명을 사용해야 합니다. 둘 다 1년 동안 캐시되면 배포 시 사용자가 이전 JavaScript를 가리키는 이전 HTML 파일에 갇힐 수 있습니다.
텍스트 에셋에 압축 사용하기
CSS, JavaScript, SVG, JSON과 같은 텍스트 에셋은 잘 압축됩니다. http 블록에서 Gzip을 활성화할 수 있습니다:
gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_vary on;
gzip_types
text/css
application/javascript
application/json
image/svg+xml;
JPEG, PNG, WebP, MP4 또는 zip 파일에 Gzip이 많은 도움이 될 것으로 기대하지 마세요. 이러한 형식은 이미 압축되어 있습니다. 다시 압축하려고 하면 일반적으로 CPU만 낭비됩니다.
트래픽이 많은 정적 사이트의 경우 사전 압축된 파일을 고려하세요. 빌드 프로세스에서 대용량 CSS 및 JavaScript 파일의 .gz 버전을 생성할 수 있으며, 브라우저가 Gzip을 지원할 때 Nginx가 이를 제공할 수 있습니다:
gzip_static on;
이렇게 하면 Nginx가 디스크에서 사전 압축된 파일을 읽기 때문에 런타임 압축 작업이 줄어듭니다. 에셋이 미리 빌드되고 요청 간에 변경되지 않을 때 가장 유용합니다.
압축은 에셋 전달의 한 부분일 뿐입니다. 파일 크기는 여전히 중요합니다. 사용하지 않는 JavaScript를 제거하고, 빌드 프로세스 중에 이미지를 최적화하며, 사용자에게 필요하지 않은 대용량 파일을 제공하지 마세요.
압축을 테스트할 때 Accept-Encoding 헤더를 포함하세요:
curl -I -H 'Accept-Encoding: gzip' https://example.com/assets/app.js
Content-Encoding: gzip 및 Vary: Accept-Encoding을 찾으세요. Vary 헤더는 CDN 또는 공유 캐시가 Nginx 앞에 있을 때 중요합니다. 압축된 응답과 압축되지 않은 응답이 혼합되지 않아야 하기 때문입니다.
파일 전달 및 보안 개선하기
Nginx는 기본 설정으로 정적 파일을 효율적으로 제공할 수 있지만, 프로덕션에서는 몇 가지 세부 사항이 도움이 됩니다.
첫째, 명시적으로 필요하지 않은 한 디렉토리 목록을 비활성화하세요:
autoindex off;
디렉토리 목록은 게시하려는 의도가 없는 파일 이름과 구조를 드러낼 수 있습니다.
둘째, .env, .git 및 기타 점 파일과 같은 숨김 파일에 대한 액세스를 차단하세요:
location ~ /\.(?!well-known) {
deny all;
}
.well-known에 대한 예외는 인증서 검증 및 표준 기반 파일이 해당 디렉토리를 사용할 수 있기 때문에 일반적입니다.
셋째, 정적 파일 권한이 Nginx가 파일을 읽을 수 있도록 허용하지만 웹 서버에 불필요한 쓰기 액세스를 제공하지 않는지 확인하세요. 일반적인 설정은 배포 도구가 파일을 쓰고 Nginx 작업자 사용자가 파일을 읽을 수 있도록 합니다.
넷째, MIME 유형을 확인하세요. Nginx는 일반적으로 mime.types 파일을 포함하지만, 축소된 컨테이너나 사용자 정의 빌드에서는 누락될 수 있습니다. CSS가 text/plain으로 제공되면 브라우저가 이를 거부하거나 다르게 동작할 수 있습니다.
다음을 사용하세요:
include /etc/nginx/mime.types;
default_type application/octet-stream;
마지막으로, 에셋에 대한 반복적인 404 응답에 대해 로그를 확인하세요. 이는 종종 배포가 존재하지 않는 파일을 참조하거나, 캐시가 여전히 이전 파일 이름을 가리키거나, alias 경로가 잘못되었음을 의미합니다.
정적 전달이 느리게 느껴진다면, 찾을 수 있는 모든 튜닝 지시문을 복사하는 것부터 시작하지 마세요. 먼저 문제가 실제로 Nginx인지 확인하세요. 대용량 이미지, 최적화되지 않은 프론트엔드 번들, 원격 스토리지 마운트, CDN 캐시 미스가 서버 블록의 미세 최적화 누락보다 더 일반적인 원인입니다.
빠른 로컬 확인을 위해:
curl -o /dev/null -s -w 'status=%{http_code} size=%{size_download} time=%{time_total}\n' https://example.com/assets/app.js
그런 다음 CDN 로그, 브라우저 개발자 도구 또는 사용자와 동일한 지역의 요청과 비교하세요. Nginx의 빠른 응답과 브라우저의 느린 응답은 일반적으로 전달 경로의 다른 곳을 가리킵니다.
도움이 필요할 때
정적 파일이 공유 스토리지, 마운트된 볼륨, 객체 스토리지 게이트웨이 또는 Nginx 앞의 CDN에서 제공되는 경우 DevOps 엔지니어를 참여시키세요. 최상의 캐싱 전략은 Nginx 서버 블록뿐만 아니라 전체 전달 경로에 따라 달라집니다.
또한 사용자가 배포 후 오래된 JavaScript를 보고하는 경우 도움을 받아야 합니다. 이는 일반적으로 캐시 규칙과 파일 이름 버전 관리 전략이 일치하지 않음을 의미합니다.
Nginx로 정적 파일을 제공하는 것은 경로가 예측 가능하고, 캐시 수명이 파일 이름 전략과 일치하며, 텍스트 에셋이 압축될 때 가장 잘 작동합니다. 누락된 파일을 표시하고, 숨김 파일을 보호하며, 각 구성 변경 후 헤더를 테스트하세요. 깔끔한 정적 설정은 이동 부품을 추가하지 않고 사이트를 더 빠르게 만듭니다.