คำตอบสั้น ๆ: ตั้งค่า HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy และ Content-Security-Policy บน Nginx, Apache, Plesk และ IIS อย่างปลอดภัย
คู่มือนี้อธิบาย HTTP Security Headers ทีละขั้นตอน พร้อมคำสั่งที่ใช้ได้จริงและปัญหาที่พบบ่อย
HTTP Security Headers คือคำสั่งที่เซิร์ฟเวอร์ส่งไปพร้อมหน้าเว็บ เพื่อบอกเบราว์เซอร์ว่าควรป้องกันผู้ใช้อย่างไร เช่น ต้องใช้ HTTPS เสมอ ห้ามนำหน้าเว็บไปฝังใน iframe ของเว็บอื่น หรือห้ามเดาชนิดไฟล์เอง Header เหล่านี้ไม่ได้แทนการอัปเดตระบบหรือเขียนโค้ดให้ปลอดภัย แต่ช่วยปิดช่องทางโจมตีฝั่งเบราว์เซอร์หลายแบบ เช่น Clickjacking และ XSS บางรูปแบบ
บทความนี้อธิบาย Header ที่ควรมี ความหมาย ค่าที่แนะนำ และวิธีตั้งบน Nginx, Apache, Plesk และ IIS พร้อมข้อควรระวังที่ทำให้เว็บพังได้ถ้าตั้งเร็วเกินไป
สิ่งที่ต้องเตรียม (HTTP Security Headers)
- เว็บไซต์ที่ใช้ HTTPS ได้สมบูรณ์แล้ว (ทุกหน้าและทุก Subdomain ที่เกี่ยวข้อง)
- สิทธิ์แก้ไฟล์ตั้งค่า Web Server หรือไฟล์
.htaccessหรือสิทธิ์เข้า Plesk - สำเนาไฟล์ตั้งค่าเดิม และช่วงเวลาที่ทดสอบเว็บได้หลังเปลี่ยน
Header ที่แนะนำและความหมาย (HTTP Security Headers)
| Header | ค่าที่แนะนำเป็นจุดเริ่มต้น | ป้องกันอะไร |
|---|---|---|
Strict-Transport-Security | max-age=300 แล้วค่อยเพิ่มเป็น max-age=31536000 | บังคับให้เบราว์เซอร์ใช้ HTTPS เสมอ ป้องกันการดักเปลี่ยนเป็น HTTP |
X-Content-Type-Options | nosniff | ห้ามเบราว์เซอร์เดาชนิดไฟล์ เช่นตีความไฟล์ข้อความเป็นสคริปต์ |
X-Frame-Options | SAMEORIGIN | Clickjacking โดยห้ามเว็บอื่นนำหน้าเราไปฝังใน iframe |
Referrer-Policy | strict-origin-when-cross-origin | ไม่ส่ง URL เต็ม (ซึ่งอาจมีข้อมูลส่วนตัว) ไปให้เว็บอื่น |
Permissions-Policy | camera=(), microphone=(), geolocation=() | ปิดการใช้ฟีเจอร์ของเบราว์เซอร์ที่เว็บไม่ได้ใช้ |
Content-Security-Policy | เริ่มแบบ Content-Security-Policy-Report-Only | กำหนดว่าโหลดสคริปต์ รูป ฟอนต์ จากที่ไหนได้บ้าง ลดผลกระทบของ XSS |
Header X-XSS-Protection เลิกใช้แล้ว เบราว์เซอร์รุ่นปัจจุบันไม่รองรับ ไม่จำเป็นต้องตั้ง ถ้าเครื่องมือตรวจบางตัวแนะนำ ให้ตั้งเป็น 0 หรือไม่ตั้งเลย ส่วน X-Frame-Options ใช้ร่วมกับ frame-ancestors 'self' ใน CSP ได้ เบราว์เซอร์รุ่นใหม่จะยึด CSP เป็นหลัก
ขั้นตอนที่ 1: ตรวจ Header ที่มีอยู่ตอนนี้ (HTTP Security Headers)
curl -sI https://example.com/
ดูว่ามี Header ใดในตารางอยู่แล้วบ้าง เพราะแผงควบคุม, ปลั๊กอินความปลอดภัย หรือ CDN อาจส่งมาให้แล้ว ถ้าตั้งซ้ำ เบราว์เซอร์จะได้รับสองค่า ซึ่งบาง Header (เช่น CSP) จะถูกนำมาใช้ร่วมกันแบบเข้มขึ้นจนเว็บพังได้
ขั้นตอนที่ 2: ตั้งค่าบน Nginx (HTTP Security Headers)
ใส่ในบล็อก server { } ที่ฟัง 443 ของเว็บ
add_header Strict-Transport-Security "max-age=300" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
คำว่า always ทำให้ส่ง Header แม้หน้าตอบกลับเป็น Error เช่น 404 หรือ 500
ข้อควรระวังที่พบบ่อยที่สุด: ถ้าบล็อก location ใดมี add_header ของตัวเอง (เช่นตั้ง Cache-Control ให้ไฟล์รูป) Header ทั้งหมดจากระดับ server จะไม่ถูกส่งใน location นั้นเลย วิธีแก้คือเก็บ Header ไว้ในไฟล์แยก แล้ว include ทั้งในระดับ server และใน location ที่มี add_header ของตัวเอง
sudo nano /etc/nginx/snippets/security-headers.conf
ย้ายบรรทัด add_header ทั้งห้าบรรทัดข้างต้นไปไว้ในไฟล์นี้ ในบล็อก server เหลือเพียง include snippets/security-headers.conf; และใน location ที่มี Header ของตัวเองให้เขียนแบบนี้
location ~* \.(jpg|jpeg|png|webp|svg|css|js)$ {
include snippets/security-headers.conf;
add_header Cache-Control "public, max-age=2592000";
}
ตรวจและโหลดค่าใหม่
sudo nginx -t
sudo systemctl reload nginx
บน AlmaLinux/Rocky ไม่มีโฟลเดอร์ snippets มาให้ สร้างเองด้วย sudo mkdir -p /etc/nginx/snippets แล้ว include ด้วย Path เต็ม
ขั้นตอนที่ 3: ตั้งค่าบน Apache หรือไฟล์ .htaccess
ต้องเปิดโมดูล mod_headers ก่อน บน Ubuntu ใช้
sudo a2enmod headers
sudo systemctl reload apache2
บน AlmaLinux/Rocky โมดูลนี้เปิดอยู่แล้ว ตรวจด้วย httpd -M | grep headers จากนั้นใส่ใน Virtual Host หรือด้านบนของไฟล์ .htaccess
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=300"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
</IfModule>
ใช้ set ไม่ใช่ add เพื่อแทนที่ค่าเดิมแทนการส่งซ้ำ ถ้าแก้ Virtual Host ให้รัน apachectl configtest ก่อน reload
ขั้นตอนที่ 4: บน Plesk และ IIS
บน Plesk ที่เปิด Proxy mode ใช้วิธีของ Apache ใน .htaccess ได้ หรือไปที่ Websites & Domains เลือกโดเมน เปิด Apache & nginx Settings แล้วใส่คำสั่ง add_header ในช่อง Additional nginx directives ส่วน HSTS อาจตั้งจากส่วนขยาย SSL It! ได้ในบางเวอร์ชัน ถ้าตั้งที่นั่นแล้ว อย่าตั้งซ้ำในที่อื่น
บน IIS ใส่ในไฟล์ web.config ของเว็บ
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="X-Content-Type-Options" value="nosniff" />
<add name="X-Frame-Options" value="SAMEORIGIN" />
<add name="Referrer-Policy" value="strict-origin-when-cross-origin" />
</customHeaders>
</httpProtocol>
</system.webServer>
หรือตั้งผ่าน IIS Manager ที่ไอคอน HTTP Response Headers ของเว็บ สำหรับ HSTS บน IIS 10 รุ่น Windows Server 2019 ขึ้นไป ตั้งได้ในหน้าตั้งค่า HSTS ของ Site