[{"data":1,"prerenderedAt":239},["ShallowReactive",2],{"article-\u002Fkho-skill\u002Fportless":3},{"id":4,"title":5,"body":6,"category":219,"description":220,"draft":221,"extension":222,"level":223,"meta":224,"minutes":225,"navigation":226,"outcomes":227,"path":228,"pinned":221,"premium":221,"prereq":229,"quiz":230,"repo":203,"seo":231,"stem":232,"tags":233,"updatedAt":236,"video":237,"__hash__":238},"articles\u002Fkho-skill\u002Fportless.md","Portless — mỗi app local một địa chỉ có tên",{"type":7,"value":8,"toc":211},"minimark",[9,34,41,46,66,70,73,102,106,109,131,137,154,163,167,176,197,207],[10,11,12,13,17,18,21,22,25,26,30,31],"p",{},"Khi để AI dựng nhiều dự án cùng lúc, bạn sẽ gặp cảnh này: app này chạy ở ",[14,15,16],"code",{},"localhost:3000",", app kia ",[14,19,20],{},"3001",", app nọ ",[14,23,24],{},"5173","… vài hôm sau không nhớ cổng nào là app nào, và có lúc hai app giành cùng một cổng. ",[27,28,29],"strong",{},"Portless"," giải quyết đúng chuyện đó: mỗi app có một địa chỉ có tên, ví dụ ",[14,32,33],{},"https:\u002F\u002Fmyapp.localhost",[10,35,36],{},[37,38],"img",{"alt":39,"src":40},"Trước là ba cổng localhost khó nhớ, sau là mỗi app một địa chỉ có tên và có khoá HTTPS","\u002Fimages\u002Fkho-skill\u002Fportless-1.webp",[42,43,45],"h2",{"id":44},"dùng-để-làm-gì","Dùng để làm gì",[47,48,49,57,60,63],"ul",{},[50,51,52,53,56],"li",{},"Thay số cổng bằng tên dễ nhớ (",[14,54,55],{},"myapp.localhost","), không phải nhớ hay đoán cổng",[50,58,59],{},"Có HTTPS sẵn trên máy bạn: portless tạo chứng chỉ riêng và thêm vào kho tin cậy của hệ điều hành, nên trình duyệt không cảnh báo",[50,61,62],{},"Hữu ích khi thử các tính năng đòi HTTPS (đăng nhập, cookie bảo mật…) mà vẫn chạy local",[50,64,65],{},"Dùng với git worktree: tên nhánh thành tên miền phụ, nên mỗi nhánh có địa chỉ riêng, không đụng nhau",[42,67,69],{"id":68},"cài-đặt","Cài đặt",[10,71,72],{},"Cần Node.js 24 trở lên (bạn đã cài ở bài chuẩn bị máy)",[74,75,80],"pre",{"className":76,"code":77,"language":78,"meta":79,"style":79},"language-bash shiki shiki-themes github-dark-dimmed","npm install -g portless\n","bash","",[14,81,82],{"__ignoreMap":79},[83,84,87,91,95,99],"span",{"class":85,"line":86},"line",1,[83,88,90],{"class":89},"s7B5-","npm",[83,92,94],{"class":93},"sP_-H"," install",[83,96,98],{"class":97},"sDGpf"," -g",[83,100,101],{"class":93}," portless\n",[42,103,105],{"id":104},"dùng-thử","Dùng thử",[10,107,108],{},"Đặt tên app rồi theo sau là lệnh chạy bình thường của dự án:",[74,110,112],{"className":76,"code":111,"language":78,"meta":79,"style":79},"portless myapp npm run dev\n",[14,113,114],{"__ignoreMap":79},[83,115,116,119,122,125,128],{"class":85,"line":86},[83,117,118],{"class":89},"portless",[83,120,121],{"class":93}," myapp",[83,123,124],{"class":93}," npm",[83,126,127],{"class":93}," run",[83,129,130],{"class":93}," dev\n",[10,132,133,134,136],{},"Mở ",[14,135,33],{}," trong trình duyệt là thấy app. Muốn portless tự đoán tên theo thư mục dự án thì dùng:",[74,138,140],{"className":76,"code":139,"language":78,"meta":79,"style":79},"portless run npm run dev\n",[14,141,142],{"__ignoreMap":79},[83,143,144,146,148,150,152],{"class":85,"line":86},[83,145,118],{"class":89},[83,147,127],{"class":93},[83,149,124],{"class":93},[83,151,127],{"class":93},[83,153,130],{"class":93},[155,156,157],"meo",{},[10,158,159,160],{},"Nhờ AI cài giúp: dán cho Claude Code câu \"Cài portless theo README của vercel-labs\u002Fportless rồi chạy dự án này qua nó\". Nhớ đọc lệnh nó định chạy, nhất là lệnh có ",[14,161,162],{},"sudo",[42,164,166],{"id":165},"lưu-ý-trước-khi-dùng","Lưu ý trước khi dùng",[168,169,170],"canh-bao",{},[10,171,172,173,175],{},"Portless cần quyền quản trị (",[14,174,162],{},") trên macOS\u002FLinux để dùng cổng 443, và thêm một chứng chỉ vào hệ thống. Chỉ làm khi bạn hiểu mình đang cho phép điều gì",[47,177,178,181,191],{},[50,179,180],{},"Công cụ còn ở bản trước 1.0, cách hoạt động có thể thay đổi giữa các bản; nếu làm việc nhóm, nên thống nhất cùng một bản",[50,182,183,184,187,188],{},"Trình duyệt Safari đôi khi cần chỉnh thêm tệp ",[14,185,186],{},"\u002Fetc\u002Fhosts"," để nhận tên ",[14,189,190],{},".localhost",[50,192,193,194,196],{},"Chưa cần thiết nếu bạn chỉ chạy một app: lúc đó ",[14,195,16],{}," vẫn ổn",[10,198,199,200],{},"Chi tiết đầy đủ và các tuỳ chọn (tên miền phụ, chia sẻ qua Tailscale\u002Fngrok, monorepo) có trong ",[201,202,206],"a",{"href":203,"rel":204},"https:\u002F\u002Fgithub.com\u002Fvercel-labs\u002Fportless",[205],"nofollow","README chính thức",[208,209,210],"style",{},"html pre.shiki code .s7B5-, html code.shiki .s7B5-{--shiki-default:#F69D50}html pre.shiki code .sP_-H, html code.shiki .sP_-H{--shiki-default:#96D0FF}html pre.shiki code .sDGpf, html code.shiki .sDGpf{--shiki-default:#6CB6FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":79,"searchDepth":212,"depth":212,"links":213},3,[214,216,217,218],{"id":44,"depth":215,"text":45},2,{"id":68,"depth":215,"text":69},{"id":104,"depth":215,"text":105},{"id":165,"depth":215,"text":166},"môi trường dev","Thay localhost:3000 bằng địa chỉ dễ nhớ như https:\u002F\u002Fmyapp.localhost, có HTTPS sẵn, không còn đụng cổng khi chạy nhiều app cùng lúc",false,"md","dev",{},4,true,[],"\u002Fkho-skill\u002Fportless",[],[],{"title":5,"description":220},"kho-skill\u002Fportless",[219,234,235],"localhost","https","2026-10-09",null,"thksd2A_gxh-RQIqx39C3R6zLVPKskWTaU3IP524HFI",1791569278408]