Quay lại blog
27 tháng 10, 2025Sergei Solod6 phút đọc

Website Next.js tĩnh của tôi nhận hàng nghìn lượt quét lỗ hổng mỗi ngày. Nginx gần như không bận tâm

Log của tôi đầy những probe tìm WordPress, PHP backdoor, .env và .git/config. Trên stack Next.js tĩnh + Nginx, phần lớn chỉ kết thúc bằng miss và 404 có chi phí thấp, trong khi tải CPU không thay đổi lúc tôi quan sát. Đây là điều trải nghiệm đó thực sự nói về bảo mật website tĩnh — và những gì nó không chứng minh.

Bảo mậtNext.jsNginxDevOpsStatic HostingAttack Surface

Mỗi ngày, server log của tôi ghi lại hàng nghìn lượt quét lỗ hổng tự động. Nhìn các request thì khá đáng ngại:

  • /wp-admin/wp.php
  • /111.php
  • /code.php
  • /.git/config
  • /i.php
  • /admin
  • /api/.env
  • /kal.php

Điều làm tôi bất ngờ không phải số lượng mà là phản ứng của server. Trong lúc quan sát traffic này, tải CPU không có thay đổi rõ rệt.

Quan sát đó là thật, nhưng cách giải thích ban đầu của tôi quá tuyệt đối. Static delivery không phải lá chắn ma thuật và 404 cũng không miễn phí. Lợi thế thực tế của kiến trúc này cụ thể hơn: rất nhiều opportunistic probe có chi phí thấp vì gần như không có server-side application logic nào trong public request path để chúng chạm tới.

Đây là probe, không phải bằng chứng hệ thống đã bị compromise

Nhìn /wp-admin/wp.php hoặc /111.php, rất dễ gọi mọi request là “exploit”. Về kỹ thuật thì không chính xác. Phần lớn những gì tôi thấy là automated probing: bot thử các filename và endpoint phổ biến rồi xem cái gì phản hồi.

Một số path rõ ràng nhắm tới WordPress hoặc PHP. Những path như /.git/config/api/.env tìm configuration hoặc secret bị public nhầm. /admin còn generic hơn và có thể là route hợp lệ. Một request đáng ngờ cho tôi biết scanner đang tìm gì, chứ không chứng minh vulnerability đó tồn tại.

Trong trường hợp của tôi, kiến trúc mới là yếu tố quyết định: website public là HTML tĩnh được pre-render bằng Next.js và phục vụ trực tiếp bởi Nginx.

Vì sao traffic này rẻ trên stack của tôi

Ở những page đang nói tới, không có PHP runtime hay database đứng sau mỗi request. Nginx phục vụ các file đã tồn tại. Nếu scanner yêu cầu /111.php nhưng file đó không có, không có PHP application nào boot, không có code WordPress được execute và không có database query. Request chỉ miss rồi kết thúc bằng 404.

Điều đó thay đổi cost model. Trong ứng dụng động, một request có thể đi qua routing, framework, authentication, rendering, database hoặc service khác. Static path của tôi có ít moving parts hơn rất nhiều. Nginx vẫn phải nhận connection, xử lý HTTP, tìm resource, ghi log và gửi response, nhưng lượng application work nhỏ.

Tôi cũng dùng aggressive caching cho site, rất hữu ích với static delivery thông thường. Tuy nhiên tôi sẽ không gán mọi random 404 cho cache. Một URI rác chưa từng thấy vẫn có thể đi qua processing bình thường của Nginx và filesystem lookup trước khi trả 404.

Điểm cần sửa quan trọng: static không có nghĩa là “mặc định an toàn”

Đây là phần tôi sẽ viết cẩn thận hơn bây giờ. Static architecture loại nhiều server-side component khỏi public request path, nhưng không loại bỏ công việc security.

  • File bị expose vẫn là file bị expose. Nếu .git, .env, backup hoặc secret vô tình nằm trong public directory, Nginx có thể phục vụ nó hiệu quả như HTML nếu configuration không chặn.
  • Nginx, TLS, hệ điều hành và network vẫn là attack surface. Chúng vẫn cần được patch và configure đúng.
  • Client-side code vẫn có thể có vulnerability. Static HTML không tự động làm JavaScript, browser-side auth logic hay third-party script an toàn.
  • Resource exhaustion vẫn có thể xảy ra. Connection, bandwidth, TLS handshake, worker capacity, log I/O và disk space đều hữu hạn. Đủ traffic vẫn có thể ảnh hưởng site tĩnh.

Vì vậy kết luận đúng không phải “những request này không quan trọng”, mà là: nhiều application-specific probe không tìm thấy gì để execute trong kiến trúc này.

“Hàng nghìn scan mỗi ngày” có thể nghe đáng sợ hơn tải thực tế

Log khiến Internet background noise trông rất dữ vì mỗi probe thất bại đều thành một dòng. Nhưng tổng số theo ngày không tự nói lên operational impact. Vài nghìn request rải trong 24 giờ rất khác với cùng số đó dồn vào một burst ngắn.

CPU cũng chỉ là một signal. Trong trường hợp của tôi nó vẫn ổn định, đây là evidence hữu ích cho thấy traffic đó không tạo compute pressure đáng kể ở thời điểm quan sát. Nhưng nó không chứng minh chi phí ở resource khác bằng 0. Để đánh giá đúng tôi còn xem request rate, bandwidth, active connections, Nginx worker utilization, response latency, log volume và disk usage.

Tôi học được gì khi nhìn noise này

Bài học lớn nhất là kiến trúc có thể khiến cả một nhóm automated attack trở nên ít liên quan mà không cần rule riêng cho từng scanner signature. Bot có thể tìm WordPress và PHP backdoor cả ngày; nếu public request path không có WordPress hay PHP thì các probe đó gần như không có gì để tận dụng.

Nhưng giờ tôi không còn đánh đồng “không liên quan đến stack của mình” với “traffic vô hại”. Checklist hiện tại của tôi:

  • Biết runtime và service nào thực sự reachable từ public request path.
  • Để secret, repository metadata, backup và build artifact ngoài web root hoặc block rõ ràng.
  • Xem repeated 404 probe là Internet background noise bình thường cho tới khi có evidence khác về targeting hoặc impact.
  • Không chỉ nhìn CPU rồi kết luận abusive traffic là miễn phí.
  • Vẫn patch Nginx, OS, TLS library và dependency dù application là static.

Trải nghiệm này không chứng minh điều gì

Tôi không chạy controlled benchmark và không mô phỏng DDoS. Tôi chỉ quan sát production logs và server load khi hàng nghìn scanner request đến mỗi ngày. Điều đó đủ cho kết luận thực tế về setup của tôi, không đủ để chứng minh quy luật “static luôn thắng”.

Điều tôi có thể xác nhận chính xác hơn là: trên Next.js static export được Nginx phục vụ, phần lớn scanner traffic tôi thấy kết thúc bằng low-cost file miss và 404 thay vì chạm tới PHP runtime hay database-backed application. Tải CPU không thay đổi trong lúc tôi quan sát.

Đó là lợi thế resilience mà tôi thực sự quan tâm. Ít moving parts hơn không làm hệ thống invulnerable; nó chỉ cho random Internet noise ít nơi hơn để biến thành application work thật.