Nginx

Regular Expression ใน Nginx Configuration — ใช้ Regex อย่างเชี่ยวชาญ

Regular Expression (Regex) เป็นเครื่องมือที่มีประสิทธิภาพสำหรับการจัดการ URL routing, rewriting, และ conditional logic ใน Nginx Configuration ผู้บริหารเซิร์ฟเวอร์ที่เข้าใจ Regex อย่างลึกซึ้งสามารถสร้างการกำหนดค่า Nginx ที่ยืดหยุ่น เข้มงวด และมีประสิทธิภาพสูง บทความนี้จะแนะนำการใช้ Regular Expression ใน Nginx พร้อมตัวอย่างเชิงปฏิบัติและเทคนิคการปรับปรุงประสิทธิภาพ

ความรู้พื้นฐานเกี่ยวกับ Regular Expression ใน Nginx

Nginx ใช้ PCRE (Perl Compatible Regular Expressions) library สำหรับการสนับสนุน Regex ซึ่งให้ความสามารถมากมายเหนือ basic pattern matching ใน Nginx มี 2 ตัวดำเนินการหลักที่ใช้สำหรับ location matching:

  • ~ (tilde) — Regex matching ที่ case-sensitive
  • ~* (tilde-asterisk) — Regex matching ที่ case-insensitive

นอกจากนี้ยังมี negation operators สำหรับการ match ที่ไม่ตรงกับ pattern:

  • !~ (not match, case-sensitive) — หาก URI ไม่ตรงกับ pattern
  • !~* (not match, case-insensitive) — หาก URI ไม่ตรงกับ pattern (ไม่สนใจตัวอักษรพิมพ์)

Syntax ของ PCRE ใน Nginx

PCRE (Perl Compatible Regular Expressions) ที่ Nginx ใช้รองรับสัญลักษณ์และเมตาคำที่ครอบคลุม ตัวอักษรพิเศษทั่วไป ได้แก่:

สัญลักษณ์ ความหมาย ตัวอย่าง
. ตัวอักษรใดๆ (ยกเว้น newline) a.c → abc, adc, aXc
* ซ้ำ 0 ครั้งหรือมากกว่า ab*c → ac, abc, abbc
+ ซ้ำ 1 ครั้งหรือมากกว่า ab+c → abc, abbc
? ซ้ำ 0 หรือ 1 ครั้ง ab?c → ac, abc
^ จุดเริ่มต้นของ string ^hello → hello world
$ จุดสิ้นสุดของ string \.pdf$ → file.pdf
[abc] ตัวอักษรใดตัวหนึ่งใน set [0-9]+ → 123, 456
() Capturing group สำหรับ subpattern (file|page) → file, page
| Alternation (OR) jpg|png|gif → jpg, png, gif
\d ตัวเลข [0-9] \d{3} → 123
\w Word character [a-zA-Z0-9_] \w+ → hello_123
\s Whitespace \s+ → space, tab

Location Block ด้วย Regex

ใน Nginx location block คือส่วนหลักที่ใช้ Regex สำหรับการ routing request ไปยัง server block ต่างๆ ขึ้นอยู่กับ URI pattern

ลำดับความสำคัญของ Location Matching

Nginx ประเมินผล location block ตามลำดับต่อไปนี้:

  1. Exact match (=)
  2. Prefix match (^~)
  3. Regex match (~, ~*)
  4. Prefix match

ตัวอย่าง Location Block ด้วย Regex

# Match URI ที่เริ่มต้นด้วย /api/ แต่ case-insensitive
location ~* ^/api/ {
    proxy_pass http://api_backend;
}

# Match URI ที่ลงท้ายด้วย .php (case-insensitive)
location ~* \.php$ {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
}

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

# Match numeric IDs (/user/123, /product/456)
location ~ ^/user/([0-9]+)$ {
    proxy_pass http://user_service;
}

# Exclude certain paths from rewrite
location !~ ^/admin/ {
    try_files $uri $uri/ /index.php?$args;
}

Regex ใน Rewrite Rules

Directive rewrite ใช้ Regex เพื่อแปลง URL หรือ URI เป็นรูปแบบอื่น Syntax คือ:

rewrite regex replacement [flag];

Flags ที่ใช้บ่อย:

  • last — หยุด processing และ restart location matching
  • break — หยุด rewrite processing
  • redirect — ส่ง HTTP 302 redirect
  • permanent — ส่ง HTTP 301 permanent redirect

ตัวอย่าง Rewrite Rules

# เปลี่ยน /old-page/ เป็น /new-page/
rewrite ^/old-page/(.*)$ /new-page/$1 permanent;

# เปลี่ยน /products/123 เป็น /shop?id=123
rewrite ^/products/([0-9]+)$ /shop?id=$1 last;

# เปลี่ยน /blog/post-title เป็น /blog/index.php?post=post-title
rewrite ^/blog/([a-zA-Z0-9_-]+)$ /blog/index.php?post=$1 last;

# เอา trailing slash ออก (ยกเว้น root)
rewrite ^/(.*)/$ /$1 permanent;

# Redirect HTTP ไป HTTPS โดยใช้ regex
if ($scheme != "https") {
    rewrite ^(.*)$ https://$host$1 permanent;
}

# Redirect subdomain ไป main domain
if ($host ~ ^www\.(.+)$ ) {
    set $host_without_www $1;
    rewrite ^(.*)$ https://$host_without_www$1 permanent;
}

Capturing Groups และ Named Captures

Capturing groups (enclosed in parentheses) ช่วยให้คุณสามารถจับส่วนของ URL และใช้มันในการ rewrite ได้

Capturing Groups ทั่วไป

ใช้ $1, $2, $3... เพื่อหมายถึง captured groups:

# ตัวอย่าง 1: Capture username จาก URL
# /user/john/profile → /profile.php?user=john
rewrite ^/user/([a-zA-Z0-9_]+)/profile$ /profile.php?user=$1 last;

# ตัวอย่าง 2: Capture file extension
# /files/document.pdf → /download.php?file=document.pdf
rewrite ^/files/(.+\.(pdf|doc|xls))$ /download.php?file=$1 last;

# ตัวอย่าง 3: Capture multiple segments
# /blog/2025/04/my-post → /blog/?year=2025&month=04&slug=my-post
rewrite ^/blog/([0-9]{4})/([0-9]{2})/([a-z0-9-]+)$ /blog/?year=$1&month=$2&slug=$3 last;

# ตัวอย่าง 4: Capture domain subdomain
# เปลี่ยน sub.example.com/path เป็น example.com/sub/path
rewrite ^/(.*)$ /subdomain/$1 last;
if ($host ~ ^([^.]+)\.example\.com$ ) {
    set $subdomain $1;
    rewrite ^/(.*)$ /$subdomain/$1 last;
}

Named Captures

PCRE รองรับ named captures ซึ่งทำให้โค้ดอ่านง่ายมากขึ้น โดยใช้ syntax (?P<name>pattern):

# Named capture เพื่อความชัดเจน
# /user/john/posts → /posts.php?username=john
map $uri $username {
    ~^/user/(?P<name>[a-zA-Z0-9_]+)/posts$ $name;
}

location ~ ^/user/([a-zA-Z0-9_]+)/posts$ {
    set $username $1;
    rewrite ^/user/(?P<uname>[a-zA-Z0-9_]+)/posts$ /posts.php?username=$uname last;
}

Regex Patterns ทั่วไปสำหรับ Nginx

การ Match File Extensions

# Match image files (case-insensitive)
location ~* \.(jpg|jpeg|png|gif|webp|svg|ico)$ {
    expires 30d;
    access_log off;
}

# Match media files
location ~* \.(mp4|webm|ogg|mp3|wav|flac)$ {
    expires 7d;
}

# Match static assets
location ~* \.(js|css|woff|woff2|ttf|otf|eot)$ {
    expires 365d;
    add_header Cache-Control "public, immutable";
}

# Exclude hidden files and directories
location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}

การ Match Query Strings

# ตรวจสอบ query string parameters
if ($args ~ "utm_source=") {
    add_header X-Tracked "true";
}

# Block requests ที่มี suspicious parameters
if ($args ~* "(union|select|drop|insert)") {
    return 403;
}

# Match page parameter
if ($args ~ "page=([0-9]+)") {
    set $page_num $1;
}

# Redirect query string
location ~ ^/search$ {
    if ($args ~ "q=([^&]+)") {
        set $query $1;
        rewrite ^/search$ /results.php?query=$query? last;
    }
}

การ Match URI Patterns

# Match API endpoints
location ~ ^/api/v([0-9]+)/([a-z0-9]+)/([0-9]+)$ {
    # /api/v1/users/123 → v1, users, 123
    proxy_pass http://api_backend;
}

# Match REST style endpoints
location ~ ^/resources/([a-z]+)/([0-9]+)(/relations)?$ {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_param RESOURCE_TYPE $1;
    fastcgi_param RESOURCE_ID $2;
    fastcgi_param WITH_RELATIONS $3;
}

# Match WordPress style URLs
location ~ ^/([0-9]{4})/([0-9]{2})/([a-z0-9-]+)/?$ {
    rewrite ^/([0-9]{4})/([0-9]{2})/([a-z0-9-]+)/?$ /index.php?year=$1&month=$2&slug=$3 last;
}

# Match user profile pages
location ~ ^/@([a-zA-Z0-9_-]{3,32})(/.*)?$ {
    rewrite ^/@([a-zA-Z0-9_-]+)(/.*)?$ /profile.php?user=$1 last;
}

Regex ใน Map Directive

Directive map ใช้ regex สำหรับสร้าง key-value mapping ที่ซับซ้อนมากขึ้น:

# Map URI pattern ไปยัง backend
map $uri $backend {
    default         http://default_backend;
    ~^/api/v1/      http://api_v1_backend;
    ~^/api/v2/      http://api_v2_backend;
    ~^/admin/       http://admin_backend;
    ~*\.json$       http://json_backend;
}

server {
    location / {
        proxy_pass $backend;
    }
}

# Map User-Agent เพื่อตรวจจับ bots
map $http_user_agent $is_bot {
    default         0;
    ~*googlebot     1;
    ~*bingbot       1;
    ~*curl          1;
    ~*wget          1;
}

server {
    location / {
        if ($is_bot = 1) {
            return 403;
        }
    }
}

# Map client IP ไปยัง geographic region
map $remote_addr $geo_region {
    default                     "unknown";
    ~^192\.168\.               "internal";
    ~^10\.0\.                  "private";
}

server {
    location / {
        add_header X-Region $geo_region;
    }
}

Regex ใน If Conditions

Block if ใน Nginx สามารถใช้ regex operators (~, ~*, !~, !~*) เพื่อตรวจสอบเงื่อนไขต่างๆ:

# ตรวจสอบ request method
if ($request_method ~ ^(POST|PUT|DELETE)$ ) {
    set $is_write_request 1;
}

# ตรวจสอบ file path
if ($uri ~ \.(jpg|png|gif|ico|css|js)$ ) {
    set $is_static 1;
}

# ตรวจสอบ request header
if ($http_user_agent ~* (bot|crawler|spider) ) {
    return 403;
}

# ตรวจสอบ custom header
if ($http_x_forwarded_proto !~ https) {
    return 301 https://$server_name$request_uri;
}

# ตรวจสอบ accept header
if ($http_accept ~ application/json) {
    set $response_format json;
}

# ตรวจสอบ referer
if ($http_referer !~ ^https://example\.com ) {
    set $block_hotlink 1;
}

# Block empty referer บนบาง endpoints
if ($uri ~ ^/api/ ) {
    if ($http_referer = "" ) {
        return 401;
    }
}

การพิจารณาประสิทธิภาพเมื่อใช้ Regex

Regex มีความสำคัญต่อประสิทธิภาพของ Nginx เนื่องจากการประเมินผล pattern ที่ซับซ้อนใช้เวลาสำคัญ หัวข้อต่อไปนี้สำคัญในการปรับปรุงประสิทธิภาพ:

Prefix Matching vs Regex Matching

# ❌ ช้า: ใช้ regex สำหรับ prefix matching
location ~ ^/static/ {
    alias /var/www/static/;
}

# ✅ เร็ว: ใช้ prefix matching ที่สั้น
location /static/ {
    alias /var/www/static/;
}

# ❌ ช้า: regex ที่มี alternation มากมาย
location ~ ^/(jpg|jpeg|png|gif|webp|ico|svg|css|js|woff|woff2|ttf|otf)$ {
    alias /var/www/assets/;
}

# ✅ เร็ว: ใช้ regex ที่ simple
location ~* \.(jpg|jpeg|png|gif|webp|ico|svg|css|js|woff|woff2|ttf|otf)$ {
    alias /var/www/assets/;
}

Regex Quantifier Optimization

# ❌ Greedy matching (นำหน้า matching ช้า)
location ~ ^/uploads/.*\.php$ {
    return 403;
}

# ✅ ใช้ specific character class
location ~ ^/uploads/[^/]*\.php$ {
    return 403;
}

# ❌ Backtracking มากมาย
location ~ ^(.*)/index\.html$ {
    rewrite ^(.*)/index\.html$ $1/ last;
}

# ✅ ระบุ pattern ที่ชัดเจน
location ~ ^/[a-z0-9-]+/index\.html$ {
    rewrite ^(/[a-z0-9-]+)/index\.html$ $1/ last;
}

Cache Regex Results ด้วย Map

# แทนที่จะตรวจสอบ regex ในทุก request
# ใช้ map เพื่อ cache ผล

map $uri $route_backend {
    default             http://default;
    ~^/api/v1/         http://api_v1;
    ~^/api/v2/         http://api_v2;
}

server {
    location / {
        proxy_pass $route_backend;
    }
}

# แทนที่จะใช้ if block หลายครั้ง
# ใช้ map เพื่อ cache ผล

map $http_user_agent $request_type {
    default     "user";
    ~*bot       "bot";
    ~*crawler   "bot";
    ~*spider    "bot";
}

server {
    location / {
        if ($request_type = "bot") {
            return 403;
        }
    }
}

การ Debug Regex ด้วย Error Log

เมื่อ regex ไม่ทำงานตามคาดหวัง ใช้ error_log เพื่อติดตามปัญหา:

# ตั้งค่า error log level สูงขึ้น
error_log /var/log/nginx/debug.log debug;

# เพิ่มเติม logging สำหรับ regex matching
location ~ ^/api/ {
    # Log request URI เพื่อ debug
    rewrite ^/api/([a-z]+)/([0-9]+)$ /handler.php?resource=$1&id=$2 last;
}

# ใช้ tcpdump เพื่อ inspect requests ที่มี regex ที่ซับซ้อน
# sudo tcpdump -i lo -A 'tcp port 80 or tcp port 443'

# ใช้ curl ด้วย verbose flag
# curl -v -H "User-Agent: testbot" http://localhost/api/users/123

Best Practices สำหรับ Regex ใน Nginx

ตัวอักษรพิเศษ (Special Characters) ในการเขียน Regex ที่ถูกต้องและมีประสิทธิภาพ:

  • ใช้ prefix matching แทน regex เมื่อเป็นไปได้ — Prefix matching (~^) เร็วกว่า regex matching มาก หากคุณสามารถตัดสินใจ routing โดยใช้ prefix matching เท่านั้น ให้ใช้มัน
  • หลีกเลี่ยง backtracking — ใช้ atomic groups หรือ possessive quantifiers เพื่อลดการ backtracking
  • ระบุ character class ที่แคบลง — แทนที่จะใช้ .* ให้ใช้ [^/]* หรือ [a-z0-9]*
  • ทดสอบ regex ก่อนเผยแพร่ — ใช้เครื่องมือทดสอบ regex online เช่น regex101.com
  • ใช้ map เพื่อ cache ผลลัพธ์ — แทนที่จะตรวจสอบ regex ใน location block ให้ใช้ map
  • หลีกเลี่ยงการใช้ if block — Nginx ขอแนะนำให้หลีกเลี่ยงการใช้ if block เมื่อเป็นไปได้ ใช้ location block แทน
  • เพิ่มความคิดเห็นใน config — regex อาจสับสนได้ ให้เพิ่มความคิดเห็นอธิบาย pattern
  • ใช้ named groups สำหรับความชัดเจน — Named groups ช่วยให้โค้ด readable มากขึ้น
  • ทำให้ regex เรียบง่ายและเฉพาะเจาะจง — regex ที่สั้นและเฉพาะจะเร็วกว่าและอ่านง่ายกว่า
  • ใช้ case-insensitive matching เมื่อจำเป็น — ~* จะช้ากว่า ~ เล็กน้อย ใช้เมื่อจำเป็นเท่านั้น

ตัวอย่าง Best Practices

# ✅ ดี: ระบุ pattern ที่ชัดเจนและมีความคิดเห็น
# Match WordPress blog posts (YYYY/MM/slug format)
location ~ ^/([0-9]{4})/([0-9]{2})/([a-z0-9-]+)/?$ {
    rewrite ^/([0-9]{4})/([0-9]{2})/([a-z0-9-]+)/?$ /index.php?year=$1&month=$2&slug=$3 last;
}

# ❌ ไม่ดี: ไม่ชัดเจนและไม่มีความคิดเห็น
location ~ ^/(.*)/(.*)/(.*)/$ {
    rewrite ^/(.*)/(.*)/(.*)/$ /index.php?a=$1&b=$2&c=$3 last;
}

# ✅ ดี: ใช้ map แทน if block
map $uri $is_admin {
    default         0;
    ~^/admin/       1;
    ~^/wp-admin/    1;
}

server {
    location / {
        if ($is_admin = 1) {
            auth_basic "Admin Area";
        }
    }
}

# ✅ ดี: ใช้ prefix matching สำหรับ static files
location /static/ {
    alias /var/www/static/;
    expires 30d;
}

# ✅ ดี: ทดสอบ regex ด้วย tools
# https://regex101.com/
# Pattern: ^/user/([a-zA-Z0-9_]+)/posts$
# Test: /user/john_doe/posts → match

สรุป

Regular Expression เป็นเครื่องมือที่強力สำหรับการกำหนดค่า Nginx ที่ขั้นสูง ด้วยการใช้ regex อย่างเชี่ยวชาญ คุณสามารถสร้าง routing rules ที่ยืดหยุ่นและแม่นยำได้ อย่างไรก็ตาม regex มีค่าใช้จ่ายด้านประสิทธิภาพ ดังนั้นการเลือกใช้ regex ที่เหมาะสม การปรับให้เบาบาง และการ cache ผลลัพธ์จึงเป็นสิ่งจำเป็น

สิ่งสำคัญที่ควรจำ:

  • ใช้ prefix matching (~^) แทน regex เมื่อเป็นไปได้ เพื่อปรับปรุงประสิทธิภาพ
  • PCRE syntax ของ Nginx รองรับ features ขั้นสูงเช่น named groups และ lookahead
  • หลีกเลี่ยง if block และใช้ map directive เพื่อ cache ผลลัพธ์ regex
  • ทดสอบและ debug regex ก่อนเผยแพร่ไปยัง production
  • ให้ความคิดเห็นใน Nginx config เพื่อช่วยให้ regex อ่านและดำรงรักษาได้

ด้วยความรู้เหล่านี้ คุณสามารถใช้ regex ใน Nginx ได้อย่างมีประสิทธิภาพ และสร้างโครงสร้าง web server ที่เข้มงวดและสามารถจัดการ traffic ได้อย่างดี

แนะนำบริการของ Dot Enterprise

การตั้งค่า Nginx ด้วย Regex ที่ซับซ้อนต้องการเซิร์ฟเวอร์ที่มีประสิทธิภาพและมั่นคง Dot Enterprise (DE) ให้บริการ Cloud VPS และ Cloud Hosting ที่เหมาะสำหรับการ host Nginx server ของคุณ

Cloud VPS จาก DE มอบความสำเร็จแบบเต็มรูปแบบ — คุณสามารถเข้าถึง root access และตั้งค่า Nginx configuration ได้อย่างสมบูรณ์ พร้อมทั้งใช้ regex เพื่อสร้าง routing rules ที่ยืดหยุ่น บริการมี DDoS protection และ automatic backups เพื่อให้เซิร์ฟเวอร์ของคุณคงปลอดภัยและเสถียร

หากคุณต้องการโซลูชันที่ง่ายต่อการจัดการ Cloud Hosting ของ DE ให้บริการ managed hosting ที่มี Nginx ที่ได้รับการปรับปรุง และสามารถจัดการ advanced regex configuration ได้อย่างอัตโนมัติ

เยี่ยมชม Cloud VPS ของ DE หรือ Cloud Hosting ของ DE เพื่อเรียนรู้เพิ่มเติมและเลือกแพลนที่เหมาะสมสำหรับความต้องการของคุณ

รับการสนับสนุนระดับมืออาชีพ — ทีมของ Dot Enterprise พร้อมช่วยคุณในการปรับปรุง Nginx configuration และแก้ไขปัญหา regex ต่างๆ ด้วยประสบการณ์มากมายในการจัดการเซิร์ฟเวอร์ CloudFlare ที่ทรงประสิทธิภาพ