nginx-modular-config

จัดการ NGINX ได้อย่างสบายด้วยการแยกส่วน config อย่างเป็นระบบ

Published on
5 mins read

มาาาาจาากล่าวบทปายยยยยย....ถุ้ย! 55555

ย้อนรอยไปตอนที่แล้วที่สอนทำ LEMP Stack อย่างยากง่ายไป คือตัว NGINX อ่ะมีความสามารถที่จะสามารถจัดการ Web Server ได้หลายๆ Domain ภายในตัวมันเองได้

แต่คราวนี้ปัญหาจะมา ถ้าเราเอา config ทุกอย่างไปลงที่ nginx.conf ที่เดียวเลย เวลามีหลายๆ Domain เข้าจำนวนบรรทัดอาจพุ่งเป็นระดับพันบรรทัดได้

Server Burn

เพราะฉะนั้นริฟฟี่อัศวินม้าขาวจะมาช่วยแก้ปัญหาท่านให้เอง!!!!

โครงสร้างไฟล์

อันนี้ผมยกตัวอย่างโครงสร้างอย่างง่ายให้ดูล่ะกัน

/etc/nginx
├── sites-enabled
│   └── example.com.conf
├── snippets
│   └── nginx-status-cats
│       ├── error
│       └── error-cat.conf
├── _general.conf
├── _php56.conf
├── _php56_fastcgi.conf
├── _php70.conf
├── _php70_fastcgi.conf
├── _ssl.conf
├── _wordpress.conf
└── nginx.conf

มา! อธิบายเป็นอันต่ออันให้รู้เรื่องก่อน

site-enabled/ เป็น folder ที่จะใช้ในการตั้งค่า server แบบ 1 ไฟล์ต่อ Domain ไปเลย และเพื่อให้ง่ายต่อการหาผมก็ตั้งชื่อแบบ <YOUR DOMAIN NAME>.conf ไปเลย

snippets/ เป็น folder ที่ไว้ใช้ลง custom module ต่างๆโดยอันนี้ผมให้แสดง HTTP error status เป็นรูปแมวน่าร๊ากกกกแบบนี้ ไปหาเล่นได้บน GitHub ของผม (ขายของๆ 555)

_general.conf จะอุดมไปด้วย config พื้นฐานทุกอย่างที่แต่ละ Domain ควรมี

_php จะอุดมไปด้วย config PHP ล้วนๆ โดยเรียกผ่าน _phpXX.conf และแก้ไข config ที่ _phpXX_fastcgi.conf

_ssl.conf ระบุด้วย config SSL ทุกอย่างโดยที่สามารถเรียกไฟล์เดียวใช้กับทุกโดเมนแบบนี้ได้เพราะเรามี Wildcard Certificate เย้!!

_wordpress.conf เผื่อใครใช้ Wordpress ก็ทำ config สำเร็จรูปให้พร้อม

หลังจากนี้จะเริ่มแกะให้ดูทีละไฟล์ล่ะ พร้อมอธิบายเป็ส่วนๆ แต่ไม่ต้องห่วงมีซอสแจกให้

nginx.conf

ไฟล์แบบ basic สุดๆเลย

# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
  worker_connections 1024;
}

http {

  log_format main '$remote_addr - $remote_user [$time_local] "$request" '
  '$status $body_bytes_sent "$http_referer" '
  '"$http_user_agent" "$http_x_forwarded_for"';

  access_log /var/log/nginx/access.log main;

  charset utf-8;
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  keepalive_timeout 40;
  types_hash_max_size 2048;
  client_body_timeout 20;
  reset_timedout_connection on;
  server_tokens off;
  client_max_body_size 16m;

  include mime.types;
  default_type application/octet-stream;

  # Load modular configuration files from the /etc/nginx/conf.d directory.
  # See http://nginx.org/en/docs/ngx_core_module.html#include
  # for more information.
  include /etc/nginx/conf.d/*.conf;
  server {
    listen 80;
    listen [::]:80;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name *.example.com;
    error_log /var/log/nginx/default.error;
    access_log /var/log/nginx/default.access;
    include _ssl.conf;
    location / {
      deny all;
      return 401;
    }
    include _general.conf;
  }
  include /etc/nginx/sites-enabled/*;
}

จะเห็นว่าผมเรียก include /etc/nginx/sites-enabled/*; คือให้ import config ทุกอย่างมาจาก sites-enabled/ เวลาจะเพิ่ม-ลด Domain ก็แค่สร้าง-ลบไฟล์ง่ายๆไม่ต้องไปปวดหัวแก้หลายที่

และมี server block เอาไว้เป็น default ในกรณีที่มีเด็กเกรียนเข้ามาทาง IP ตรงๆ หรือถูกชี้มาจาก Domain ที่เราไม่ได้ตั้งค่าไว้ก็เอาอันนี้ดักไว้เลย

_php56.conf

สังเกตุเห็นว่าผมตั้งเลขเวอร์ชั่นที่ชื่อไฟล์ด้วย...ใช่ เพราะ NGINX ก็สามารถเรียกใช้ PHP ได้หลายเวอร์ชั่นได้ด้วย

# index
index index.php;

# handle .php
location ~ \.php$ {
  include _php56_fastcgi.conf;
}

ก็..ไม่มีอะไรมาก บอก index และให้ไปเอา config สำหรับไฟล์ .php ที่ _php56_fastcgi.conf

_php56_fastcgi.conf

try_files $uri =404;

# fastcgi
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors off;

fastcgi_buffer_size 128k;
fastcgi_buffers	256 16k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;

# default fastcgi_params
include fastcgi_params;

อันนี้ก็บอกว่าให้ไปเรียก FPM สำหรับ PHP 5.6 ที่ /var/run/php-fpm/php-fpm.sock และก็ config ต่างๆเพิ่มเติม

_ssl.conf

ssl_certificate /etc/letsencrypt/live/rayriffy.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rayriffy.com/privkey.pem;

ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256;
ssl_prefer_server_ciphers on;

# HSTS (1 year, preload)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;

จากตอนที่ผมสร้าง Wildcard Certificate ก็เอามาใช้ในงานนี้แหล่! ก็เรียกไฟล์ SSL ที่สร้างมาโดยจะมีทั้งตัว certificate และ private key ใส่ไว้ที่ ssl_certificate และ ssl_certificate_key ตามลำดับ

ต่อมาด้วยตอนที่สร้าง NGINX พร้อมกับ PageSpeed Module ผมเคยบ่นไว้ว่าจะใช้ TLSv1.3 ก็เอามาใช้ใน ssl_protocols นี้แหล่เพื่อเพิ่มระดับความปลอดภับของการสื่อสารผมแนะนำว่าอย่าใช้ TLSv1.1 เลคครับ แต่ก็ต้องแลกมากับ Web Browser เก่าๆจะเข้าเว็บเราไม่ได้เลย

ของแถมคือไหนๆเว็บเราก็จะใช้ HTTPS 100% ล่ะผมก็ขอแนะนำ HSTS โดยย่อมาจาก HTTP Strict Transport Security โดยมันเป็น Website header ไว้ใช้ป้องกันการโดยขโมย cookie, SSL โดนแกะ หรือถูกโจมตีวิธีต่างๆ และเป็นตัวบังคับให้ Web Browser เชื่อมต่อผ่านทาง HTTPS เท่านั้นด้วย

_general.conf

# pagespeed
pagespeed On;
pagespeed FileCachePath "/var/cache/ngx_pagespeed/";
pagespeed EnableFilters collapse_whitespace,combine_heads,flatten_css_imports,combine_css,combine_javascript,lazyload_images,resize_rendered_image_dimensions,rewrite_javascript,rewrite_images,rewrite_css;

# . files
location ~ /\. {
  deny all;
  return 401;
}

# assets, media
location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ {
  expires 7d;
  access_log off;
}

# ext include
include snippets/nginx-status-cats/error-cat.conf;

ไฟล์นี้ไม่ต้องอธิบายอะไรมาก ป้องกันการเข้าถึงไฟล์ที่ขึ้นต้นด้วย . ทั้งหมดและจัดการกับ media file ทุกอย่าง รวมถึงการตั้งค่า pagespeed ด้วย และมี nginx-status-cat เป็นของแถม

_wordpress.conf

อันนี้ไม่ขออธิบาย แต่ง่ายๆว่าป้องการโดนปลิงไฟล์ไปใช้เว็บอื่นได้

หลังจากรู้ทุกอย่างที่ควรรู้แล้วก็มาสร้าง server กันเลย

sites-enabled/example.com.conf

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;

  pagespeed Off;
  server_name example.com;
  set $base /usr/share/nginx;
  root $base/$server_name;

  error_log /var/log/nginx/example.com.error;
  access_log /var/log/nginx/example.com.access;

  index index.html index.php;

  # SSL
  include _ssl.conf;

  # $uri, index.php
  location / {
    try_files $uri $uri/ /index.php?$query_string;
  }

  include _general.conf;
  include _php56.conf;
}

# HTTP redirect
server {
  listen 80;
  listen [::]:80;

  server_name example.com;

  return 301 https://example.com$request_uri;
}

อย่างแรกเลย เพื่อให้ชัวร์ว่าคนใช้มาผ่านทาง HTTPS จริงเราปลอดภัยไว้ก่อนให้ redirect ไป HTTPS โดยอัตโนมัติถ้าผ่านมาจาก HTTP

จากนั้นก็ include ของพื้นฐานเข้าไป _general.conf,_php56.conf,_ssl.conf ถ้าเปิดเป็น Wordpress ก็อย่างลืมใส่ _wordpress.conf ไปด้วยล่ะกัน

ตำแหน่งการจัดเก็บไฟล์ (root $base/$server_name;) ก็เก็บเป็นระบบเช่นกันโดยเก็บไว้ที่ /usr/share/nginx/ แล้วต่อด้วยชื่อโดเมน example.com ได้ path เต็มๆเป็น /usr/share/nginx/example.com

สรุป

จากที่บอกเป็นไฟล์ต่อไฟล์ก็น่าจะเห็นว่าไฟล์เยอะสัส แต่แลกมาด้วยความง่ายในการแก้ไข และความที่เป็น module สามารถถอดเข้า-ออก ได้ตามสะดวก

และ...อย่าลืม! reload nginx ด้วยนะครับ 5555

$ systemctl restart nginx

Bonus

Source

ไปเอาโค๊ดที่ GitHub ได้เลย!!

rayriffy/nginx-modular-config