본문으로 건너뛰기
Advertisement

.htaccess 활용

.htaccess(Hypertext Access)는 디렉터리별로 Apache 설정을 재정의할 수 있는 분산 설정 파일입니다. 메인 설정 파일을 수정할 권한이 없는 공유 호스팅 환경에서 특히 유용합니다. URL 리다이렉트, 접근 제어, 캐시 헤더, HTTPS 강제 설정 등 다양한 기능을 구현할 수 있습니다.


.htaccess 동작 원리

Apache가 HTTP 요청을 처리할 때 요청된 파일까지의 경로에 있는 모든 디렉터리를 거슬러 올라가며 .htaccess 파일을 읽습니다.

요청: GET /blog/2024/post.html

Apache가 읽는 .htaccess 순서:
1. /.htaccess
2. /blog/.htaccess
3. /blog/2024/.htaccess ← 가장 구체적인 설정이 우선 적용

성능 주의: .htaccess는 요청마다 파일을 읽어 처리하므로 많은 I/O를 발생시킵니다. 가능하면 httpd.conf에 직접 설정하고 AllowOverride None으로 .htaccess를 비활성화하는 것이 성능에 유리합니다.

활성화 조건

.htaccess가 동작하려면 해당 디렉터리에 AllowOverride가 설정되어 있어야 합니다.

# httpd.conf 또는 VirtualHost 설정에서
<Directory "/var/www/html">
AllowOverride All # .htaccess 모든 지시어 허용
</Directory>

URL 리다이렉트 (mod_rewrite)

mod_rewrite는 Apache 가장 강력한 모듈 중 하나로, URL을 패턴 기반으로 변환·리다이렉트합니다.

기본 mod_rewrite 활성화

RewriteEngine On

HTTP → HTTPS 강제 리다이렉트

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

www → non-www 리다이렉트

RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]

non-www → www 리다이렉트

RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

SPA(React/Vue) 클라이언트 라우팅

RewriteEngine On
# 실제 파일이나 디렉터리가 있으면 그대로 제공
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# 없으면 index.html로 폴백 (React Router 처리)
RewriteRule ^ /index.html [L]

또는 Apache 2.4.16+에서는 더 간단하게:

FallbackResource /index.html

URL 정리 (Pretty URL)

RewriteEngine On
# example.com/blog/123 → example.com/blog.php?id=123
RewriteRule ^blog/([0-9]+)/?$ blog.php?id=$1 [L,QSA]

# example.com/user/홍길동 → example.com/user.php?name=홍길동
RewriteRule ^user/(.+)/?$ user.php?name=$1 [L,QSA]

RewriteRule 플래그 정리

플래그설명
R=301301 영구 리다이렉트
R=302302 임시 리다이렉트
L마지막 규칙 (이 규칙 매칭 후 더 이상 처리 안 함)
QSA기존 쿼리 스트링 보존
NC대소문자 무시
NE특수문자 이스케이프 안 함
PTPass Through — 다음 핸들러로 넘김
F403 Forbidden 반환
G410 Gone 반환

RewriteCond 변수 목록

# 자주 사용하는 RewriteCond 변수
%{HTTPS} # on / off
%{HTTP_HOST} # 요청 호스트 헤더
%{REQUEST_URI} # 요청 URI (/path?query)
%{QUERY_STRING} # 쿼리 스트링
%{REMOTE_ADDR} # 클라이언트 IP
%{REQUEST_METHOD} # GET, POST 등
%{HTTP_USER_AGENT} # User-Agent
%{REQUEST_FILENAME} # 실제 파일 경로
%{SERVER_PORT} # 서버 포트

접근 제어

# 특정 IP 차단
<RequireAll>
Require all granted
Require not ip 203.0.113.5
Require not ip 198.51.100.0/24
</RequireAll>

# 특정 IP만 허용
Require ip 127.0.0.1
Require ip 192.168.1.0/24

# 특정 User-Agent 차단
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} (BadBot|Scraper|Harvester) [NC]
RewriteRule .* - [F,L]

캐시 헤더 설정 (mod_expires, mod_headers)

# mod_expires 활성화 필요 (Ubuntu: sudo a2enmod expires)
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 week"

ExpiresByType text/html "access plus 0 seconds"
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/webp "access plus 1 month"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType application/pdf "access plus 1 month"
</IfModule>

# mod_headers로 Cache-Control 설정
<IfModule mod_headers.c>
<FilesMatch "\.(js|css)$">
Header set Cache-Control "max-age=31536000, public, immutable"
</FilesMatch>
<FilesMatch "\.(html|htm)$">
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
</FilesMatch>
</IfModule>

Gzip 압축 (.htaccess)

<IfModule mod_deflate.c>
# 텍스트 기반 파일 압축
AddOutputFilterByType DEFLATE text/html text/plain text/css
AddOutputFilterByType DEFLATE application/javascript application/json
AddOutputFilterByType DEFLATE image/svg+xml font/woff font/woff2

# 이미 압축된 파일 제외
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png|webp|zip|gz|bz2)$ no-gzip dont-vary

# 구형 브라우저 호환
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
</IfModule>

보안 헤더 설정

<IfModule mod_headers.c>
# 클릭재킹 방지
Header always set X-Frame-Options "SAMEORIGIN"

# MIME 타입 스니핑 방지
Header always set X-Content-Type-Options "nosniff"

# XSS 필터 활성화
Header always set X-XSS-Protection "1; mode=block"

# HSTS (HTTPS 강제 1년)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

# Referrer 정책
Header always set Referrer-Policy "strict-origin-when-cross-origin"

# 서버 정보 숨기기
Header unset Server
Header unset X-Powered-By
</IfModule>

에러 페이지 커스터마이징

# 커스텀 에러 페이지
ErrorDocument 400 /errors/400.html
ErrorDocument 401 /errors/401.html
ErrorDocument 403 /errors/403.html
ErrorDocument 404 /errors/404.html
ErrorDocument 500 /errors/500.html
ErrorDocument 503 /errors/503.html

디렉터리 인덱스 설정

# 기본 인덱스 파일 지정
DirectoryIndex index.html index.php index.htm

# 디렉터리 목록 표시 비활성화 (보안)
Options -Indexes

.htaccess vs httpd.conf — 선택 기준

상황권장 방법
공유 호스팅 (루트 접근 불가).htaccess
전용 서버 (httpd.conf 직접 수정 가능)httpd.conf (성능 우선)
CMS(WordPress, Drupal).htaccess (프레임워크에서 자동 생성)
개발·테스트 환경.htaccess (빠른 변경 적용)
운영 환경 고성능httpd.conf + AllowOverride None

정리

  • .htaccess: 디렉터리별 분산 설정, 공유 호스팅·CMS에 적합
  • mod_rewrite: HTTPS 강제, SPA 라우팅, URL 정리 등 URL 변환 핵심
  • mod_expires + mod_headers: 캐시·보안 헤더 설정
  • 운영 환경에서는 가능하면 httpd.conf에 통합 + AllowOverride None으로 성능 최적화
Advertisement