タイトル: .htaccess
SEOタイトル: Apache .htaccess 完全ガイド — リライト・認証・セキュリティ
| この記事の要点 |
|
.htaccess の基本
Apache HTTP Server でディレクトリごとに設定を上書きできる仕組みです。配置したディレクトリと配下に効果が及びます。サーバ全体の設定 (httpd.conf) を書き換えなくて済むのが利点。
有効化(AllowOverride)
# /etc/apache2/apache2.conf または /etc/httpd/conf/httpd.conf
AllowOverride All # ★ これが None だと .htaccess 無視
Require all granted
# 設定後リロード
# sudo systemctl reload apache2
# sudo systemctl reload httpd
URL リライト (RewriteEngine)
# /var/www/html/.htaccess
RewriteEngine On
# 1. すべての URL を index.php に転送(フレームワーク前提:Laravel/CodeIgniter 等)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [L,QSA]
# 2. /old → /new (恒久リダイレクト)
RewriteRule ^old/(.*)$ /new/$1 [R=301,L]
# 3. www あり → www なし統一
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^(.*)$ https://example.com/$1 [R=301,L]
# 4. HTTP → HTTPS 強制
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# 5. クエリ文字列で振り分け
RewriteCond %{QUERY_STRING} ^id=(\d+)$
RewriteRule ^show\.php$ /post/%1? [R=301,L]
リダイレクト (mod_alias)
# 301 恒久リダイレクト
Redirect 301 /old-page /new-page
Redirect 301 /docs https://docs.example.com
# 302 一時リダイレクト
Redirect 302 /campaign /landing
# ディレクトリ全体
RedirectMatch 301 ^/blog/(.*)$ https://example.com/articles/$1
Basic 認証
# .htaccess
AuthType Basic
AuthName "Restricted Area" # ダイアログに表示される名称
AuthUserFile /etc/apache2/.htpasswd # ★ Web 公開外に置く
Require valid-user
# 特定ユーザだけ
Require user alice bob
# グループで管理
AuthGroupFile /etc/apache2/.htgroup
Require group admins
パスワードファイル作成:
# 新規作成(-c)
sudo htpasswd -c /etc/apache2/.htpasswd alice
# New password: ...
# Re-type new password: ...
# ユーザ追加(-c 無し)
sudo htpasswd /etc/apache2/.htpasswd bob
# 削除
sudo htpasswd -D /etc/apache2/.htpasswd bob
# 中身確認
cat /etc/apache2/.htpasswd
# alice:$apr1$xyz...
IP アドレス制限
# Apache 2.4+ 構文
# 特定 IP のみ許可
Require ip 192.168.1.0/24
Require ip 203.0.113.5
# 特定 IP のみ拒否(その他許可)
Require all granted
Require not ip 203.0.113.99
Require not ip 10.0.0.0/8
# 認証 + IP のいずれかで OK
Require ip 192.168.1.0/24
Require valid-user
# Apache 2.2 以前(古い記法、混在環境注意)
Order Deny,Allow
Deny from all
Allow from 192.168.1
ディレクトリ一覧の無効化
# index.html が無いディレクトリで一覧表示されるのを防ぐ
Options -Indexes
# シンボリックリンクも禁止したい
Options -Indexes -FollowSymLinks
# CGI 実行禁止
Options -ExecCGI
# .htaccess を無効化(サーバ管理者用)
AllowOverride None
特定ファイル / 拡張子の保護
# .env / .git / .htaccess 自体を Web からアクセス不可に
Require all denied
Require all denied
Require all denied
# 拡張子で
Require all denied
# ディレクトリ単位
Require all denied
キャッシュ / 圧縮
# 静的ファイルのキャッシュヘッダ
ExpiresActive On
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType text/css "access plus 1 week"
ExpiresByType application/javascript "access plus 1 week"
ExpiresByType text/html "access plus 5 minutes"
# gzip 圧縮
AddOutputFilterByType DEFLATE text/html text/css application/javascript application/json text/xml
# Brotli (mod_brotli 必要)
AddOutputFilterByType BROTLI_COMPRESS text/html text/css application/javascript
セキュリティヘッダ
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=()"
Header always unset X-Powered-By
# CSP(厳密化したい場合)
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'"
カスタムエラーページ
ErrorDocument 404 /errors/404.html
ErrorDocument 403 /errors/403.html
ErrorDocument 500 /errors/500.html
ErrorDocument 503 /maintenance.html
Laravel 用 .htaccess 標準
# Laravel public/.htaccess(公式デフォルト)
Options -MultiViews -Indexes
RewriteEngine On
# Authorization ヘッダの引き継ぎ
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# /index.php へリダイレクトする末尾スラッシュ
RewriteRule ^(.*)/$ /$1 [L,R=301]
# 存在しないファイル / ディレクトリは index.php へ
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
Nginx での同等設定
Nginx には .htaccess の概念がありません。すべて server / location ブロックで書きます:
server {
listen 80;
server_name example.com;
# HTTPS 強制
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
root /var/www/html/public;
index index.php;
# 全リクエストを index.php へ(Laravel)
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# ディレクトリ一覧 OFF
autoindex off;
# 隠しファイル拒否
location ~ /\. {
deny all;
}
# Basic 認証
location /admin {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
}
# PHP
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
デバッグ: 反映されているか確認
# 構文チェック
sudo apachectl configtest
# モジュール確認
apachectl -M | grep rewrite # rewrite_module (shared) と出れば OK
apachectl -M | grep headers
apachectl -M | grep auth_basic
# 有効化
sudo a2enmod rewrite headers
sudo systemctl restart apache2
# .htaccess が読まれているか
echo "Deny from all" >> .htaccess # 一時的に
# → 全リクエストが 403 になれば反映されている
FAQ
Q: .htaccess の変更を即時反映するには?
A: 不要。配置・更新と同時に Apache が読み込みます。サーバ再起動は不要です。
Q: .htaccess を使うと遅い?
A: ディレクトリ階層をたどるたびに読み込むので性能を気にする本番では httpd.conf に書くのが定石です。
Q: 反映されない
A: ① AllowOverride All 確認 ② a2enmod rewrite ③ Apache 再起動 ④ .htaccess の文法エラー(error.log 参照)。