Hiện tại trang medium.com khá là khó truy cập ở Việt Nam, mọi người muốn truy cập hầu như đều phải dùng đến VPN.
Cũng không rõ nguyên nhân là gì, nhưng mình nghĩ là có thể làm 1 tool để proxy request đến Medium và trả về nội dung để có thể xem được. Ý tưởng này không phải của mình, trước đó có 1 site là medium0.com đã làm rồi, mình cũng sử dụng được 1 thời gian, nhưng gần đây thì không dùng được nữa, không biết là do chi phí duy trì hay vấn đề gì nữa. Do đó, mình sẽ tự làm 1 cái để dùng cũng như chia sẻ với bạn bè. Nếu có vấn đề về bản quyền hay bất cứ vấn đề gì khác thì mình sẽ đóng lại, chỉ để dùng một mình thôi 😀
Trước khi bắt tay vào code thì cùng ngâm cứu xem để làm được tool này thì mình cần xử lý những vấn đề gì. Luồng tổng quan sẽ như thế này:
Tiếp theo là đi xem thử 1 bài xem nội dung Medium đang trả ra thế nào. Mình thử với bài này: https://medium.com/upday-devs/kubernetes-on-a-high-traffic-environment-3-key-takeaways-39d3852fb515. À từ đoạn này phải bật VPN lên nhé.
Xem qua source và test thử thì thấy Medium render từ server rồi, mấy cái JS kia không cần load cũng được luôn. Vậy công việc của mình là chỉ cần lấy HTML này và trả về cho user luôn, không phải xử lý gì thêm.
Cách để proxy requests thì mình dùng cách giống với đã làm ở phần Custom domain cho Uptimerobot, nginx muôn năm.
Để cấu hình nginx proxy request đến medium.com thì cũng khá đơn giản, chỉ cần cấu hình proxy_pass
và proxy_ssl_server_name on;
server {
listen 80 default_server;
server_name _;
location / {
proxy_pass https://medium.com;
proxy_ssl_server_name on;
}
}
Do mình chạy Nginx bằng docker container và chỉ có 1 server block nên không cần phải cấu hình server_name
. Ngoài ra, mình có sử dụng OrbStack để chạy docker trên MacOS nên chỉ cần chạy nginx container với tên là mediumz
thì có thể truy cập qua domain: https://mediumz.orb.local/.
Thử ngay với bài ở trên: https://mediumz.orb.local/upday-devs/kubernetes-on-a-high-traffic-environment-3-key-takeaways-39d3852fb515
Tada, được luôn rồi nè. Nhưng chớ vội mừng, phải check thêm xem mấy cái ảnh trên trang là dùng domain nào, nếu tắt VPN đi thì có xem được không.
Inspect lên thì thấy Medium dùng domain miro.medium.com
cho link ảnh. Thử tắt VPN đi xem thử và kết quả là hẹo luôn.
Để proxy được cả link ảnh thì sẽ cần làm 2 việc:
- Khi trả ra HTML thì thay thế tất cả link
miro.medium.com
thànhmiro.mediumz.orb.local
- Update nginx để proxy request đến
miro.medium.com
Nginx config được sửa thành
server {
listen 80 default_server;
server_name _;
location / {
proxy_pass https://medium.com;
proxy_ssl_server_name on;
proxy_set_header Accept-Encoding "";
sub_filter 'miro.medium.com' 'miro.mediumz.orb.local';
sub_filter_once off;
}
}
server {
listen 80;
server_name miro.mediumz.orb.local;
location / {
proxy_pass https://miro.medium.com;
proxy_ssl_server_name on;
}
}
Mình dùng hàm sub_filter
của module ngx_http_sub_module sẽ thay thế tất cả (sub_filter_once off;
) miro.medium.com
thành miro.mediumz.orb.local
. Chú ý mình có set thêm Accept-Encoding "";
vào proxy request để response trả về từ medium.com là dạng plain text thì mới replace được. Nói thêm 1 chút về Accept-Encoding
, Nginx sẽ forward phần lớn header (bao gồm Accept-Encoding
) và đa số các trình duyệt đều truyền Accept-Encoding: gzip, deflate, br
, khi đó response sẽ được nén giúp giảm dung lượng truyền tải. Trong trường hợp này mình đang cần sửa response, nên set Accept-Encoding "";
giúp mình nhận về plain text response để sửa sau đó.
Còn proxy request đến miro.medium.com
thì đơn giản rồi, mình không giải thích thêm.
Kết quả sau khi sửa config, link ảnh đã được thay thế bằng link proxy, đảm bảo sẽ tải được ảnh mà không cần VPN.
Đến đây là xong mất tiêu rồi, mỗi tội là chưa đủ 100 dòng code. Thôi bonus thêm Dockerfile
để build image nginx kèm ngx_http_sub_module vậy:
# Dockerfile
FROM nginx:stable as builder
WORKDIR /tmp
RUN export nginx_version=$(nginx -v 2>&1 | awk '{split($0, a); print a[3]}' | awk '{split($0, a, "/"); print a[2]}') && \
apt-get update && \
apt-get install -y curl gnupg2 ca-certificates lsb-release git gcc libpcre3 libpcre3-dev zlib1g zlib1g-dev build-essential && \
echo "deb http://nginx.org/packages/debian `lsb_release -cs` nginx" \
| tee /etc/apt/sources.list.d/nginx.list && \
curl -fsSL https://nginx.org/keys/nginx_signing.key | apt-key add - && \
git clone https://github.com/yaoweibin/ngx_http_substitutions_filter_module.git && \
curl https://nginx.org/download/nginx-$nginx_version.tar.gz -o nginx.tar.gz && \
echo https://nginx.org/download/nginx-$nginx_version.tar.gz && \
tar -xzvf nginx.tar.gz && \
cd nginx-$nginx_version && ./configure --with-compat --add-dynamic-module=../ngx_http_substitutions_filter_module && make modules && \
cp /tmp/nginx-$nginx_version/objs/ngx_http_subs_filter_module.so /tmp/ngx_http_subs_filter_module.so
FROM nginx:stable-alpine as runner
COPY --from=builder /tmp/ngx_http_subs_filter_module.so /etc/nginx/modules/
COPY templates /etc/nginx/templates
Và cuối cùng vẫn chưa được 100 dòng code 😀
Code mình có để ở trên github, mọi người cùng tham khảo. Tuy nhiên đây chỉ là source code demo cho bài viết này thôi nhé.