Chuyển đến nội dung chính

第10課:NGINX 重寫與重新導向

關於 NGINX 重寫與重新導向的課程——使用正規表達式的重寫規則、return 與 rewrite 指令比較、Location 匹配模式(精確、前綴、正規表達式)、try_files 指令與條件式重新導向。URL 操作、SEO 友好重新導向及正式環境最佳實踐指南。

🔒 DevSecOps — 第10課 第10課:NGINX 重寫與重新導向

Nginx 從入門到進階

第3部分:安全性與效能

xdev.asia

1. 使用正規表達式的重寫規則

Rewrite rules cho phép modify URLs trước khi xử lý requests.

1.1. Cú pháp Rewrite

rewrite regex replacement [flag];

regex: Regular expression pattern to match

replacement: New URL

flag: Optional flag (break, last, redirect, permanent)

Flags:

  • break - Stop processing, use rewritten URI
  • last - Stop processing rewrite rules, re-search locations
  • redirect - Return 302 temporary redirect
  • permanent - Return 301 permanent redirect

1.2. Basic Rewrite Examples

server {
listen 80;
server_name example.com;

# Simple rewrite
rewrite ^/old-page$ /new-page permanent;

# Rewrite with regex capture
rewrite ^/user/(.*)$ /profile/$1 permanent;

# Multiple captures
rewrite ^/post/([0-9]+)/([a-z]+)$ /article/$2/$1 permanent;

location / {
    root /var/www/html;
}

}

Example với captures:

server {
# /product/123 → /item.php?id=123
rewrite ^/product/([0-9]+)$ /item.php?id=$1 last;

# /category/electronics/page/5 → /cat.php?name=electronics&page=5
rewrite ^/category/([^/]+)/page/([0-9]+)$ /cat.php?name=$1&page=$2 last;

# /blog/2024/12/my-post → /blog.php?year=2024&month=12&slug=my-post
rewrite ^/blog/([0-9]{4})/([0-9]{2})/(.+)$ /blog.php?year=$1&month=$2&slug=$3 last;

}

1.3. Rewrite Flags

break flag:

location /api/ {
# Rewrite và dừng processing
rewrite ^/api/v1/(.*)$ /v1/$1 break;

# Continue với rewritten URI
proxy_pass http://backend;

}

Request: /api/v1/users

Rewritten: /v1/users

Proxied to: http://backend/v1/users

last flag:

server {
# Rewrite và re-search locations
rewrite ^/old/(.*)$ /new/$1 last;

location /new/ {
    # This location will be matched after rewrite
    root /var/www/html;
}

}

Request: /old/page

Rewritten: /new/page

Re-search locations, matches /new/

redirect flag (302):

server {
# Temporary redirect
rewrite ^/temp/(.*)$ /permanent/$1 redirect;
}

Returns HTTP 302 Found

Browser redirects to new URL

permanent flag (301):

server {
# Permanent redirect (SEO-friendly)
rewrite ^/old-site/(.*)$ https://newsite.com/$1 permanent;
}

Returns HTTP 301 Moved Permanently

Search engines update their index

1.4. Regex Patterns

Common patterns:

server {
# Match digits
rewrite ^/product/([0-9]+)$ /item/$1 last;

# Match letters
rewrite ^/user/([a-z]+)$ /profile/$1 last;

# Match alphanumeric
rewrite ^/page/([a-zA-Z0-9]+)$ /content/$1 last;

# Match anything except slash
rewrite ^/category/([^/]+)$ /cat/$1 last;

# Match with specific length
rewrite ^/code/([0-9]{6})$ /verify/$1 last;

# Optional parts
rewrite ^/blog/([0-9]+)(/.*)?$ /post/$1$2 last;

# Case-insensitive (use ~* in location)
location ~* ^/PAGE/(.*)$ {
    rewrite ^ /page/$1 permanent;
}

}

Special characters:

# Escape special chars: . * + ? [ ] ^ $ ( ) { } | \

Match literal dot

rewrite ^/file.txt$ /document.txt last;

Match query string (use $args)

rewrite ^/search$ /search.php?$args last;

Preserve query string

rewrite ^/old$ /new permanent; # Automatically preserves ?param=value

1.5. Multiple Rewrites

server {
listen 80;
server_name example.com;

# Sequential rewrites
rewrite ^/products/(.*)$ /items/$1 last;

# This won't execute because of 'last' flag above
rewrite ^/items/(.*)$ /products/$1 last;

location /items/ {
    root /var/www/html;
}

}

Correct way for chained rewrites:

server {
# First rewrite
location /old/ {
rewrite ^/old/(.*)$ /temp/$1 last;
}

# Second rewrite
location /temp/ {
    rewrite ^/temp/(.*)$ /new/$1 last;
}

# Final location
location /new/ {
    root /var/www/html;
}

}

1.6. Conditional Rewrites

server {
# Rewrite based on conditions

# If not HTTPS, redirect
if ($scheme != "https") {
    rewrite ^ https://$host$request_uri permanent;
}

# If mobile user agent
if ($http_user_agent ~* (mobile|android|iphone)) {
    rewrite ^/$ /mobile/ last;
}

# If specific referer
if ($http_referer ~* google.com) {
    rewrite ^/landing$ /landing-google last;
}

# Multiple conditions
set $redirect 0;
if ($scheme != "https") {
    set $redirect 1;
}
if ($host !~* ^www\.) {
    set $redirect "${redirect}1";
}
if ($redirect = "11") {
    rewrite ^ https://www.$host$request_uri permanent;
}

}


2. Return vs Rewrite Directive

2.1. Return Directive

Return is faster and more efficient than rewrite for simple redirects.

server {
listen 80;
server_name example.com;

# Simple return
location /old-page {
    return 301 /new-page;
}

# Return with variable
location /redirect {
    return 301 https://example.com$request_uri;
}

# Return with custom response
location /api/health {
    return 200 "OK\n";
}

# Return JSON
location /api/status {
    default_type application/json;
    return 200 '{"status":"ok","timestamp":"$time_iso8601"}';
}

}

2.2. Return Status Codes

server {
# 301 - Permanent redirect (SEO)
location /old {
return 301 /new;
}

# 302 - Temporary redirect
location /temp {
    return 302 /temporary-location;
}

# 307 - Temporary redirect (preserves method)
location /preserve {
    return 307 /new-location;
}

# 308 - Permanent redirect (preserves method)
location /permanent-preserve {
    return 308 /new-permanent;
}

# 200 - Success
location /ok {
    return 200 "Success";
}

# 204 - No Content
location /nocontent {
    return 204;
}

# 400 - Bad Request
location /bad {
    return 400 "Bad Request";
}

# 403 - Forbidden
location /forbidden {
    return 403 "Access Denied";
}

# 404 - Not Found
location /notfound {
    return 404 "Not Found";
}

# 500 - Internal Server Error
location /error {
    return 500 "Internal Server Error";
}

# 503 - Service Unavailable
location /maintenance {
    return 503 "Under Maintenance";
}

}

2.3. Return vs Rewrite Performance

# GOOD - Fast, efficient
location /old-page {
return 301 /new-page;
}

SLOWER - Regex processing overhead

location /old-page { rewrite ^/old-page$ /new-page permanent; }

GOOD - Simple return for redirects

if ($host != "www.example.com") { return 301 https://www.example.com$request_uri; }

SLOWER - Regex rewrite

if ($host != "www.example.com") { rewrite ^ https://www.example.com$request_uri permanent; }

2.4. When to Use Each

Use return when:

  • Simple redirects
  • No regex needed
  • Better performance required
  • Returning custom content

Use rewrite when:

  • Need regex capture groups
  • Complex URL transformations
  • Need to preserve parts of URL
  • Using flags (break, last)

Examples:

server {
# Use return - simple redirect
location = /about {
return 301 /about-us;
}

# Use rewrite - need regex captures
location /product/ {
    rewrite ^/product/([0-9]+)$ /item.php?id=$1 last;
}

# Use return - redirect to external URL
location /blog {
    return 301 https://blog.example.com;
}

# Use rewrite - transform URL structure
location /old-api/ {
    rewrite ^/old-api/v1/(.*)$ /api/v2/$1 break;
    proxy_pass http://backend;
}

}


3. Location Matching

Location matching xác định cách Nginx xử lý different URL patterns.

3.1. Location Modifiers

# No modifier - Prefix match
location /images/ {
# Matches: /images/*, /images/photo.jpg
}

= - Exact match (highest priority)

location = /about { # Matches: /about only # NOT: /about/, /about?page=1 }

^~ - Prefix match, stop regex checking

location ^~ /static/ { # Matches: /static/* # Stops checking regex locations }

~ - Case-sensitive regex

location ~ .php$ { # Matches: .php files (case-sensitive) # Matches: file.php, script.PHP (no) }

~* - Case-insensitive regex

location * .(jpg|png|gif)$ { # Matches: .jpg, .JPG, .Jpg (all cases) }

3.2. Location Priority Order

Priority (highest to lowest):

  1. Exact match =
  2. Prefix match with ^
  3. Regular expression or * (first match wins)
  4. Prefix match (longest match wins)

Example:

server {
listen 80;

# Priority 1 - Exact match
location = /test {
    return 200 "Exact match: /test\n";
}

# Priority 2 - Prefix (stop regex)
location ^~ /test {
    return 200 "Prefix ^~: /test*\n";
}

# Priority 3 - Regex
location ~* ^/test {
    return 200 "Regex ~*: /test*\n";
}

# Priority 4 - Prefix
location /test {
    return 200 "Prefix: /test*\n";
}

# Default
location / {
    return 200 "Default: /\n";
}

}

Test results:

curl http://localhost/test

→ "Exact match: /test"

curl http://localhost/test123

→ "Prefix ^: /test*" (if ^ exists)

→ "Regex : /test" (if no ^)

curl http://localhost/other

→ "Default: /"

3.3. Prefix Matching

server {
# Longest prefix wins
location /api/ {
return 200 "API root\n";
}

location /api/v1/ {
    return 200 "API v1\n";
}

location /api/v2/ {
    return 200 "API v2\n";
}

}

/api/test → "API root"

/api/v1/users → "API v1"

/api/v2/products → "API v2"

3.4. Regex Matching

server {
# Case-sensitive regex
location ~ ^/API/ {
return 200 "Uppercase API\n";
}

# Case-insensitive regex
location ~* ^/api/ {
    return 200 "Any case API\n";
}

# File extension regex
location ~ \.(jpg|png|gif)$ {
    return 200 "Image file\n";
}

# Complex regex
location ~ ^/blog/([0-9]{4})/([0-9]{2})/(.+)$ {
    return 200 "Blog post: $1-$2-$3\n";
}

}

3.5. Nested Locations

server {
location /api/ {
# Outer location

    # Nested location
    location ~ \.json$ {
        # Matches: /api/*.json
        return 200 "JSON API\n";
    }
    
    location ~ \.xml$ {
        # Matches: /api/*.xml
        return 200 "XML API\n";
    }
    
    # Default for /api/
    return 200 "API default\n";
}

}

3.6. Named Locations

server {
location / {
try_files $uri $uri/ @backend;
}

# Named location (cannot be accessed directly)
location @backend {
    proxy_pass http://backend_server;
}

# Another example
error_page 404 = @notfound;

location @notfound {
    return 404 "Custom 404 page\n";
}

}

3.7. Complete Location Example

server {
listen 80;
server_name example.com;
root /var/www/html;

# Exact match - homepage
location = / {
    index index.html;
}

# Exact match - specific file
location = /favicon.ico {
    log_not_found off;
    access_log off;
}

# Prefix match with stop regex
location ^~ /static/ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

# Regex - PHP files
location ~ \.php$ {
    fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

# Regex - Image files
location ~* \.(jpg|jpeg|png|gif|ico|svg)$ {
    expires 30d;
    add_header Cache-Control "public";
}

# Prefix match - API
location /api/ {
    proxy_pass http://api_backend/;
}

# Default location
location / {
    try_files $uri $uri/ =404;
}

# Deny hidden files
location ~ /\. {
    deny all;
}

}


4. Try_files Directive

Try_files checks for files in order and uses the first one found.

4.1. Basic Try_files

location / {
# Try file, then directory, then 404
try_files $uri $uri/ =404;
}

Request: /page

Tries:

1. /page (file)

2. /page/ (directory with index)

3. Returns 404

4.2. Try_files với Fallback

# Single Page Application (SPA)
location / {
try_files $uri $uri/ /index.html;
}

Request: /about

Tries:

1. /about (file)

2. /about/ (directory)

3. /index.html (fallback)

WordPress/PHP applications

location / { try_files $uri $uri/ /index.php?$args; }

4.3. Try_files với Named Location

location / {
try_files $uri $uri/ @backend;
}

location @backend { proxy_pass http://backend_server; }

Request: /api/users

Tries:

1. /api/users (file)

2. /api/users/ (directory)

3. @backend (proxy to backend)

4.4. Multiple Fallbacks

location / {
# Try multiple files
try_files $uri $uri/index.html $uri.html @backend;
}

location @backend { proxy_pass http://backend; }

Request: /page

Tries:

1. /page

2. /page/index.html

3. /page.html

4. @backend

4.5. Try_files with Custom Response

location / {
try_files $uri $uri/ /404.html =404;
}

Or return custom content

location / { try_files $uri $uri/ =404;

error_page 404 = @notfound;

}

location @notfound { return 404 "File not found\n"; }

4.6. Try_files Patterns

Static site:

location / {
root /var/www/html;
try_files $uri $uri/ =404;
}

React/Vue/Angular SPA:

location / {
root /var/www/app;
try_files $uri $uri/ /index.html;
}

WordPress:

location / {
try_files $uri $uri/ /index.php?$args;
}

location ~ .php$ { fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; }

API with fallback:

location /api/ {
# Try cached response, then proxy
try_files /cache$uri @api;
}

location @api { proxy_pass http://api_backend; }

Multiple static directories:

location /assets/ {
# Try multiple roots
try_files /public$uri /static$uri =404;
}

5. 條件式重新導向

5.1. If Directive

# IMPORTANT: If is evil in Nginx (use sparingly)

Prefer map, return, or rewrite when possible

server { # Redirect non-www to www if ($host !~* ^www.) { return 301 https://www.$host$request_uri; }

# Redirect HTTP to HTTPS
if ($scheme = http) {
    return 301 https://$host$request_uri;
}

# Redirect based on user agent
if ($http_user_agent ~* (mobile|android|iphone)) {
    return 302 /mobile;
}

# Redirect old domain
if ($host = old.example.com) {
    return 301 https://new.example.com$request_uri;
}

}

5.2. Map-based Redirects

http {
# Map for redirects (better than if)
map $request_uri $redirect_uri {
/old-page    /new-page;
/old-about   /about-us;
/old-contact /contact;
default      "";
}

server {
    if ($redirect_uri != "") {
        return 301 $redirect_uri;
    }
}

}

5.3. Geo-based Redirects

http {
geo $country {
default US;
192.0.2.0/24 UK;
198.51.100.0/24 JP;
}

map $country $redirect_domain {
    US example.com;
    UK uk.example.com;
    JP jp.example.com;
}

server {
    listen 80;
    
    if ($host != $redirect_domain) {
        return 302 https://$redirect_domain$request_uri;
    }
}

}

5.4. Time-based Redirects

http {
map $time_iso8601 $maintenance {
default 0;
# Maintenance window: 2024-12-03 02:00 to 04:00
~^2024-12-03T0[2-3] 1;
}

server {
    if ($maintenance) {
        return 503;
    }
    
    error_page 503 /maintenance.html;
    location = /maintenance.html {
        root /var/www/errors;
    }
}

}

5.5. Argument-based Redirects

server {
# Redirect based on query parameter
if ($arg_lang = "fr") {
return 302 /fr$request_uri;
}

if ($arg_lang = "es") {
    return 302 /es$request_uri;
}

# Redirect if parameter missing
if ($arg_id = "") {
    return 400 "ID parameter required";
}

}

5.6. Complex Conditions

server {
# Multiple conditions (AND logic)
set $redirect 0;

if ($scheme = http) {
    set $redirect "${redirect}1";
}

if ($host !~* ^www\.) {
    set $redirect "${redirect}1";
}

if ($redirect = "11") {
    return 301 https://www.$host$request_uri;
}

}


6. Real-world Examples

6.1. SEO-friendly Redirects

server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;

# HTTP to HTTPS (301 permanent)
return 301 https://example.com$request_uri;

}

server { listen 443 ssl http2; server_name www.example.com;

# www to non-www (301 permanent)
return 301 https://example.com$request_uri;

}

server { listen 443 ssl http2; server_name example.com;

# Old URLs to new structure
rewrite ^/blog/([0-9]+)/([0-9]+)/(.+)$ /articles/$1-$2-$3 permanent;

# Product URLs
rewrite ^/product-([0-9]+)\.html$ /products/$1 permanent;

# Category URLs
rewrite ^/cat-([a-z-]+)$ /category/$1 permanent;

location / {
    root /var/www/html;
    try_files $uri $uri/ =404;
}

}

6.2. Migration from Old Site

http {
# Bulk redirects using map
map $request_uri $new_uri {
/old-about.html      /about;
/old-contact.html    /contact;
/products.html       /shop;
/blog/post1.html     /blog/post-1;
/blog/post2.html     /blog/post-2;
# ... hundreds more
default              "";
}

server {
    listen 443 ssl http2;
    server_name example.com;
    
    # Apply bulk redirects
    if ($new_uri != "") {
        return 301 $new_uri;
    }
    
    # Pattern-based redirects
    rewrite ^/news/([0-9]+)$ /articles/$1 permanent;
    rewrite ^/downloads/(.+)\.zip$ /files/$1 permanent;
    
    location / {
        root /var/www/html;
        try_files $uri $uri/ =404;
    }
}

}

6.3. API Versioning

server {
listen 80;
server_name api.example.com;

# Redirect v1 to v2
location /v1/ {
    rewrite ^/v1/(.*)$ /v2/$1 permanent;
}

# Current API version
location /v2/ {
    proxy_pass http://api_backend/;
}

# Default to latest version
location / {
    rewrite ^/(.*)$ /v2/$1 last;
}

}

6.4. Multi-language Sites

http {
# Detect language from Accept-Language
map $http_accept_language $lang {
default en;
~^fr fr;
~^es es;
~*^de de;
}

server {
    listen 80;
    server_name example.com;
    
    # Redirect to language-specific subdomain
    location = / {
        return 302 https://$lang.example.com;
    }
    
    # Or redirect to language path
    # location = / {
    #     return 302 /$lang/;
    # }
}

server {
    listen 80;
    server_name en.example.com;
    root /var/www/en;
}

server {
    listen 80;
    server_name fr.example.com;
    root /var/www/fr;
}

}

6.5. Mobile Redirect

http {
# Detect mobile devices
map $http_user_agent $is_mobile {
default 0;
~mobile 1;
~android 1;
~iphone 1;
~ipad 1;
}

server {
    listen 80;
    server_name example.com;
    
    # Redirect mobile users
    if ($is_mobile) {
        return 302 https://m.example.com$request_uri;
    }
    
    location / {
        root /var/www/html;
    }
}

server {
    listen 80;
    server_name m.example.com;
    root /var/www/mobile;
}

}

6.6. Maintenance Mode

server {
listen 80;
server_name example.com;
root /var/www/html;

# Enable maintenance mode
set $maintenance off;

# Whitelist admin IPs
if ($remote_addr ~ "^(192\.168\.1\.|10\.0\.0\.)") {
    set $maintenance off;
}

# Check if maintenance file exists
if (-f /var/www/maintenance.flag) {
    set $maintenance on;
}

if ($maintenance = on) {
    return 503;
}

error_page 503 @maintenance;

location @maintenance {
    rewrite ^(.*)$ /maintenance.html break;
}

location / {
    try_files $uri $uri/ =404;
}

}

6.7. A/B Testing

http {
# Split traffic 50/50
split_clients "${remote_addr}${http_user_agent}" $variant {
50% "a";
*   "b";
}

server {
    listen 80;
    server_name example.com;
    
    location / {
        if ($variant = "a") {
            rewrite ^ /variant-a last;
        }
        
        if ($variant = "b") {
            rewrite ^ /variant-b last;
        }
    }
    
    location /variant-a {
        root /var/www/test-a;
    }
    
    location /variant-b {
        root /var/www/test-b;
    }
}

}


7. Troubleshooting

7.1. Rewrite Loop Detection

# BAD - Creates infinite loop
location / {
rewrite ^(.*)$ /index.php last;
}

location /index.php { rewrite ^(.*)$ / last; }

Request: /page

Rewrite: /index.php

Rewrite: /

Rewrite: /index.php

... infinite loop (Nginx stops after 10 cycles)

Fix:

# GOOD - Use break or proper conditions
location / {
try_files $uri $uri/ /index.php?$args;
}

location ~ .php$ { fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; include fastcgi_params; }

7.2. Debug Rewrites

# Enable rewrite log
error_log /var/log/nginx/error.log notice;
rewrite_log on;

server { location / { rewrite ^/test/(.*)$ /result/$1 last; return 200 "Original URI: $uri\n"; } }

Check logs:

tail -f /var/log/nginx/error.log

7.3. Test Redirects

# Test with curl
curl -I http://example.com/old-page

Follow redirects

curl -L http://example.com/old-page

Verbose output

curl -v http://example.com/old-page

Check redirect chain

curl -IL http://example.com/old-page

7.4. Common Issues

Issue 1: Query string not preserved

# BAD - Loses query string
rewrite ^/old$ /new;

GOOD - Preserves query string automatically

rewrite ^/old$ /new permanent;

Or explicitly:

rewrite ^/old$ /new?$args permanent;

Issue 2: Wrong flag causes issues

# BAD - Uses 'last' in if
if ($host = old.com) {
rewrite ^ https://new.com$request_uri last;
}

GOOD - Use return or permanent

if ($host = old.com) { return 301 https://new.com$request_uri; }

Issue 3: Regex not matching

# BAD - Missing anchors
rewrite /old /new permanent;

Matches: /old, /cold, /folder/old

GOOD - Use anchors

rewrite ^/old$ /new permanent;

Matches: /old only

Or use exact location

location = /old { return 301 /new; }


8. Best Practices

8.1. Prefer return over rewrite

# GOOD - Fast and clear
location /old {
return 301 /new;
}

AVOID - Slower, less clear

location /old { rewrite ^/old$ /new permanent; }

8.2. Use exact matches when possible

# GOOD - Fastest matching
location = /about {
return 301 /about-us;
}

SLOWER - Regex overhead

location ~ ^/about$ { return 301 /about-us; }

8.3. Avoid if when possible

# BAD - Using if
if ($request_uri ~ ^/old) {
rewrite ^ /new permanent;
}

GOOD - Use location

location ^~ /old { rewrite ^/old(.*)$ /new$1 permanent; }

BETTER - Use return

location /old { return 301 /new; }

8.4. Use map for bulk redirects

# GOOD - Efficient for many redirects
http {
map $request_uri $redirect_uri {
/page1 /new1;
/page2 /new2;
/page3 /new3;
# ... hundreds more
}

server {
    if ($redirect_uri != "") {
        return 301 $redirect_uri;
    }
}

}

BAD - Many individual rewrites

server { rewrite ^/page1$ /new1 permanent; rewrite ^/page2$ /new2 permanent; rewrite ^/page3$ /new3 permanent; # ... hundreds more }

8.5. Document complex rewrites

server {
# Redirect old blog structure to new
# Old: /blog/2024/12/post-title
# New: /articles/2024-12-post-title
rewrite ^/blog/([0-9]{4})/([0-9]{2})/(.+)$ /articles/$1-$2-$3 permanent;

# Redirect product IDs
# Old: /product-123.html
# New: /shop/products/123
rewrite ^/product-([0-9]+)\.html$ /shop/products/$1 permanent;

}

8.6. Test thoroughly

# Create test script
#!/bin/bash

echo "Testing redirects..."

Test 1: Old to new

curl -IL http://example.com/old-page | grep "301|Location"

Test 2: HTTP to HTTPS

curl -IL http://example.com | grep "301|Location"

Test 3: www to non-www

curl -IL https://www.example.com | grep "301|Location"

echo "Tests complete!"


9. 實踐練習

練習1: Basic Redirects

  1. Create redirect từ /old-about đến /about-us
  2. Implement HTTP to HTTPS redirect
  3. Redirect www to non-www
  4. Test với curl

練習2: Regex Rewrites

  1. Rewrite /product/123 → /item/123
  2. Rewrite /blog/2024/12/post → /articles/2024-12-post
  3. Preserve query strings
  4. Test với curl

練習3: Location Matching

  1. Setup exact match cho /
  2. Setup prefix match cho /api/
  3. Setup regex match cho image files
  4. Test priority order

練習4: Try_files

  1. Setup SPA với try_files fallback
  2. Configure WordPress-style try_files
  3. Add custom 404 page
  4. Test với various URLs

練習5: Bulk Redirects

  1. Create map với 10+ redirects
  2. Implement map-based redirect logic
  3. Test all redirects
  4. Measure performance vs individual rewrites

練習6: Complex Scenario

  1. Migrate old site structure to new
  2. Implement mobile detection
  3. Add maintenance mode
  4. Setup proper error pages
  5. Test thoroughly

總結

本課所學內容:

  • ✅ Rewrite rules với regex và flags
  • ✅ Return vs rewrite performance
  • ✅ Location matching patterns và priority
  • ✅ Try_files directive và use cases
  • ✅ Conditional redirects
  • ✅ Real-world redirect scenarios
  • ✅ Troubleshooting và best practices

重要要點:

  • Prefer return over rewrite for simple redirects
  • Use exact matches when possible
  • Avoid if directive when alternatives exist
  • Use map for bulk redirects
  • Test redirects thoroughly
  • Document complex rewrite rules

下一課: Nginx với Application Stack - PHP-FPM configuration, Nginx + Node.js, Python (uWSGI/Gunicorn), Docker containers và WebSocket proxying để integrate Nginx với various backend technologies.