Bạn có một website chạy trên máy chủ của mình, ví dụ một VPS thuê ngoài. Làm sao để cả thế giới vào xem được? Cách cổ điển là mở một cổng trên máy chủ rồi trỏ tên miền vào địa chỉ IP của nó. Cách đó phơi máy chủ ra Internet: IP bị lộ, và mỗi cổng mở là một cánh cửa để người lạ dò.
Cloudflare Tunnel làm theo hướng ngược lại. Máy chủ tự nối RA Cloudflare và giữ sẵn một đường ống; người xem đi VÀO qua Cloudflare rồi mới theo ống đó về máy chủ. Kết quả là máy chủ không mở cổng vào nào, IP thật không lộ, mà website vẫn có một địa chỉ cố định và ổ khóa HTTPS.
Bài này giải thích cơ chế đằng sau chuyện đó, không đi qua các bước bấm nút. Trang bạn đang đọc là một ví dụ chạy đúng theo mô hình này, nên thỉnh thoảng sẽ lấy tên miền của nó (aiengineerblog.com) ra minh họa. Hai khái niệm nền tảng cần nắm là named tunnel và reverse proxy.
Sơ đồ trên là toàn cảnh sẽ được mổ xẻ dần: người đọc bắt tay HTTPS với lớp edge của Cloudflare, edge giải mã rồi đẩy tiếp qua đường ống vào một tiến trình tên cloudflared nằm trong máy chủ, và tiến trình đó chuyển vào service web chạy website. Đường ống được máy chủ chủ động quay ra, nên không đầu nào phải mở cổng chờ.
1. Named tunnel: đường ống có tên, có danh tính
Cloudflare Tunnel: một đường ống nối máy chủ ra hạ tầng Cloudflare bằng một kết nối đi RA. Một tiến trình tên
cloudflaredchạy trên máy chủ tự quay số ra Cloudflare và giữ sẵn đường ống, để lưu lượng từ Internet theo ống đó đi ngược vào máy chủ. Vì là kết nối đi ra chứ không phải mở cổng chờ, máy chủ không phải hé cổng nào.
Có hai kiểu tunnel, khác nhau ở chỗ có danh tính hay không.
Quick tunnel là loại ẩn danh, dùng để thử nhanh. cloudflared bắt tay mà không khai báo gì trước, Cloudflare ném lại một URL ngẫu nhiên đuôi trycloudflare.com. Không tên, không lưu cấu hình, nên khởi động lại là mất URL. Đây thường là thứ người mới gặp đầu tiên, và cũng là lý do có bài này: một địa chỉ đổi liên tục thì không dùng thật được.
Named tunnel là loại có danh tính: mình tạo trước một đường ống có tên trên tài khoản Cloudflare. Nó có một ID cố định và một token. cloudflared cầm token để chứng minh "tôi là đường ống này", rồi Cloudflare tra bảng cấu hình đã lưu sẵn để biết ai gõ tên miền nào thì đẩy vào ống nào.
Điểm mấu chốt: với named tunnel, cấu hình định tuyến nằm trên Cloudflare chứ không nằm trong câu lệnh. Muốn đổi tên miền trỏ đi đâu thì sửa ở phía Cloudflare, không đụng máy chủ. Đó chính là thứ biến một URL tạm thành một địa chỉ cố định.
2. Token: danh tính của đường ống
Cái làm nên danh tính của named tunnel là token.
Token: một chuỗi bí mật gói sẵn thông tin định danh tài khoản, ID của tunnel và một khóa bí mật.
cloudflaredtrình token ra để Cloudflare nhận đúng nó là đường ống nào. Ai có token là dựng được một đường ống mạo danh chính site đó, nên token phải giữ kín như một mật khẩu.
Vì token là bí mật, câu hỏi kỹ thuật đáng quan tâm là: làm sao đưa nó vào tiến trình cloudflared mà không để lộ. Với một dự án chạy bằng Docker, cách thường dùng là truyền token qua biến môi trường, và giá trị thật đi qua ba nơi tách bạch. Sơ đồ dưới lấy đúng cấu trúc của trang này làm ví dụ:
Đọc từ trái sang: một file cấu hình bí mật nằm trên máy chủ (ở dự án này đặt tên .env.prod) giữ giá trị token thật và không bao giờ được đưa lên Git. File mô tả Docker (compose.prod.yml, có trong Git) chỉ khai TÊN của biến, không chứa giá trị. Khi khởi động, Docker ghép tên với giá trị lấy từ file bí mật rồi đặt biến đó vào bên trong container cloudflared; chính binary cloudflared mới là thứ đọc biến và quay số ra edge.
Tách được ba vai này là hiểu bản chất: nơi khai tên (file mô tả, công khai trong Git), nơi giữ giá trị thật (file bí mật trên máy chủ), và nơi đọc rồi dùng (tiến trình lúc chạy). Lý do cố ý dùng biến môi trường thay vì gõ token thẳng vào dòng lệnh: token sẽ không lộ ra trong danh sách tiến trình đang chạy của máy.
3. TLS ở edge, HTTP bên trong
Một điểm dễ thắc mắc: đường ống trỏ tới service bằng HTTP trần (ví dụ http://web:3000), trong khi người đọc ngoài kia rõ ràng dùng HTTPS. Không mâu thuẫn, vì việc mã hóa xảy ra ở một chỗ khác.
Terminate TLS: nơi phiên mã hóa HTTPS công khai được giải mã. Với kiến trúc Cloudflare, việc này xảy ra ở edge: trình duyệt bắt tay mã hóa với Cloudflare, Cloudflare giải mã rồi mới đẩy tiếp về sau bằng đường nội bộ.
cloudflared và service web nằm chung một mạng Docker riêng bên trong máy chủ, nói với nhau bằng HTTP trần vẫn an toàn vì đoạn đó không ra Internet. Ổ khóa được Cloudflare lo ở vòng ngoài, giữa trình duyệt và edge. Nếu bắt đoạn bên trong cũng phải HTTPS thì lại đòi service nội bộ tự có chứng chỉ, đúng thứ mà cả kiến trúc này muốn tránh: máy chủ phía sau không cần giữ chứng chỉ nào.
Hệ quả kiểm chứng được bằng con mắt: khi một địa chỉ trả về HTTP/2, đó là dấu hiệu đang có mã hóa, vì trình duyệt chỉ chạy HTTP/2 trên nền TLS. Và chứng chỉ cho tên miền do Cloudflare tự cấp, tự gia hạn, nên ổ khóa luôn còn hạn mà mình không phải làm gì.
4. Public hostname và ba kiểu route
Bảng cấu hình mà Cloudflare lưu sẵn thực chất trả lời câu hỏi "ai gõ tên nào thì đẩy đi đâu". Nhưng không phải kiểu route nào cũng dành cho web công khai, và đây là chỗ hay nhầm.
Public hostname: một tên miền công khai (ví dụ
aiengineerblog.comcủa trang này) được khai để trỏ vào một service nội bộ (ví dụweb:3000). Bất kỳ ai trên Internet gõ tên đó vào trình duyệt là xem được, không cần cài gì.
Cạnh public hostname còn hai kiểu route khác, nhưng cả hai đều đòi người dùng phải cài một phần mềm client riêng của Cloudflare mới đi vào được: một kiểu định tuyến theo từng máy nội bộ, một kiểu định tuyến cả một dải địa chỉ nội bộ. Chúng dành cho việc truy cập mạng riêng có kiểm soát trong doanh nghiệp, không phải cho một blog mà ai cũng phải mở được bằng trình duyệt bất kỳ. Dấu hiệu nhận ra: nếu một route đòi "phải đi qua client của Cloudflare" thì nó không phải cửa công khai. Một blog chỉ cần đúng public hostname.
Còn một chi tiết nhỏ hay gặp: apex và www là hai địa chỉ khác nhau. Apex là tên trần (aiengineerblog.com), còn www.aiengineerblog.com là một tên con. Về mặt định tuyến, chúng là hai hostname riêng biệt, nên nếu chỉ khai một cái thì cái kia sẽ không mở được. Thường chọn apex làm địa chỉ chính vì nó ngắn gọn, còn www nếu muốn nhận thì khai thêm như một public hostname thứ hai trỏ về cùng service.
5. CORS: vì sao địa chỉ của trang phải khớp cấu hình
Khi đổi từ URL tạm sang tên miền thật, có một khái niệm phía backend nên hiểu trước để sau này không dính một lỗi khó mò: CORS.
CORS (Cross-Origin Resource Sharing): luật an toàn của trình duyệt. Khi JavaScript trên một trang gọi sang API, trình duyệt gắn thêm header ghi rõ origin của trang, rồi bắt API xác nhận có cho phép origin đó không. API chỉ trả kết quả nếu origin nằm trong danh sách cho phép.
Origin (danh tính trang): scheme cộng host, ví dụ
https://aiengineerblog.com. Không kèm path, không dấu gạch chéo cuối, vàwwwbị coi là một origin khác với apex.
Backend giữ một danh sách origin được phép gọi vào nó. Nếu danh sách còn ghi địa chỉ tạm cũ trong khi trang đã chuyển sang tên miền mới, hai origin không khớp, trình duyệt sẽ chặn phản hồi. Vì thế địa chỉ khai trong cấu hình phải khớp đúng tên miền thật. Lúc site còn thuần tĩnh, trình duyệt chưa gọi API nên lỗi này chưa xảy ra; nhưng khai đúng ngay từ đầu thì khi bật tính năng động sẽ không phải đi truy một lỗi mơ hồ.
6. Reverse proxy: Cloudflare đứng che máy chủ gốc
Tất cả những gì mô tả ở trên gộp lại thành một khái niệm có tên riêng, và nắm được tên này là có sẵn một khung để hiểu nhiều hệ thống web khác.
Reverse proxy: một lớp trung gian đứng TRƯỚC máy chủ. Khách nối vào proxy, proxy mới chuyển tiếp vào máy chủ thật phía sau, nhờ đó máy chủ thật bị giấu khỏi người dùng. Ngược với forward proxy (VPN, proxy công ty) là lớp đứng phía client để giấu người dùng khỏi máy chủ.
Cụm edge Cloudflare cộng cloudflared chính là một reverse proxy đặt trước origin.
Origin (máy chủ gốc): máy chủ thật nằm sau proxy, tức VPS. Lưu ý đây là một nghĩa khác của từ "origin" so với origin trong CORS ở mục trước (danh tính một trang). Cùng một từ, hai nghĩa tùy ngữ cảnh, rất hay gặp trong web.
Một reverse proxy thường gánh giúp máy chủ một loạt việc, và ở đây Cloudflare lo hết tại edge: kết thúc TLS (cấp và giải mã ổ khóa ngay tại edge, máy chủ gốc không cần chứng chỉ), định tuyến theo hostname (bảng public hostname quyết định tên nào vào service nào), giấu IP gốc (người ngoài chỉ thấy IP Cloudflare), chống lạm dụng và cache, cùng khả năng sửa hoặc chèn thêm vào phản hồi trên đường đi.
Hệ quả bảo mật đáng nhớ: vì chỉ có đúng một tuyến công khai (một hostname vào web:3000) và máy chủ không mở cổng nào, phần backend gần như vô hình với Internet. Ví dụ trang tài liệu API tự sinh (kiểu địa chỉ /docs của nhiều backend) chỉ tồn tại trong mạng nội bộ; gõ từ ngoài không tới. Muốn mở thêm gì ra ngoài phải chủ động khai một tuyến mới và tự gắn lớp bảo vệ cho nó. Mặc định là đóng, phải cố ý mới mở, đó là một mặc định tốt.
7. Proxy được phép sửa phản hồi: đọc tab Network
Khả năng "sửa hoặc chèn vào phản hồi" nghe trừu tượng, nhưng nhìn thấy được ngay trong tab Network của trình duyệt, nơi lộ ra hai nhóm request có tên miền khác nhau.
Nhóm thứ nhất mang chính tên miền của trang, ví dụ các file tĩnh của ứng dụng (JavaScript, CSS). Chúng phát ra từ service web, đi qua proxy trả về nên vẫn mang tên miền của mình. Tên các file này thường có một chuỗi băm dài là cố ý: đổi nội dung là đổi tên, nhờ vậy trình duyệt cache rất lâu mà không sợ dùng nhầm bản cũ.
Nhóm thứ hai mang một tên miền khác, ví dụ một script phân tích truy cập của Cloudflare. Điểm đáng chú ý: mẩu script này không nằm trong mã nguồn của trang; chính reverse proxy chèn thêm vào HTML ở edge khi trả cho trình duyệt. Đó là ví dụ sống của việc proxy sửa nội dung trên đường đi.
Mẹo đọc nhanh tab Network vì thế là nhìn cột tên miền: cùng tên miền của trang là phần ứng dụng mình phát ra (qua proxy); tên miền lạ là phần bên thứ ba, ở đây do Cloudflare thêm vào tại edge.
8. Bảng thuật ngữ nhanh
| Thuật ngữ | Nghĩa gọn |
|---|---|
| Cloudflare Tunnel | Đường ống nối máy chủ ra edge bằng kết nối đi ra, không mở cổng vào |
| cloudflared | Tiến trình chạy đường ống ở phía máy chủ |
| Quick tunnel | Tunnel ẩn danh, URL ngẫu nhiên, đổi mỗi lần khởi động lại |
| Named tunnel | Tunnel có tên và ID cố định, xác thực bằng token, hostname ổn định |
| Token | Chuỗi bí mật để cloudflared chứng minh mình là tunnel nào |
| Public hostname | Tên miền công khai trỏ vào một service nội bộ |
| Terminate TLS | Nơi phiên HTTPS được giải mã; ở đây là edge Cloudflare |
| Edge | Lớp máy chủ Cloudflare đứng trước site, nơi cấp và kết thúc TLS |
| CORS | Luật trình duyệt kiểm soát trang nào được gọi API nào |
| Origin (trang) | Scheme cộng host của một trang, ví dụ https://aiengineerblog.com |
| Reverse proxy | Lớp trung gian đứng trước máy chủ, nhận kết nối thay rồi chuyển vào |
| Forward proxy | Lớp trung gian phía client, giấu người dùng khỏi máy chủ |
| Origin (máy chủ gốc) | Máy chủ thật sau proxy; khác nghĩa origin trong CORS |
9. Liên quan
Bài này là mắt xích lý thuyết của mạch hạ tầng, giải thích cơ chế đưa một site ra Internet có tên và có ổ khóa mà không mở cổng. Nó nối với:
- Domain, DNS, TLS cho người mới: domain, DNS, nameserver và vì sao Cloudflare có sẵn HTTPS khi vừa quản DNS vừa làm edge.
- Deploy website lên VPS bằng GitHub Actions: stack chạy thật trên VPS mà tunnel đứng trước.
- Object storage cho blog: MinIO vs Cloudflare R2: cùng ý edge và reverse proxy, áp dụng cho việc phục vụ ảnh.
