[{"data":1,"prerenderedAt":367},["ShallowReactive",2],{"blog-\u002Fen\u002Fblog\u002Fconfiguration-nginx-avec-vuejs-erreur-404":3,"blog-\u002Fen\u002Fblog\u002Fconfiguration-nginx-avec-vuejs-erreur-404-surround":241,"blog-\u002Fen\u002Fblog\u002Fconfiguration-nginx-avec-vuejs-erreur-404-certifications":301},{"id":4,"title":5,"body":6,"categories":226,"cover":228,"cover_contain":152,"credly_badge_id":229,"date":230,"description":231,"extension":232,"meta":233,"navigation":152,"path":234,"related_certifications":235,"seo":237,"slug":238,"stem":239,"__hash__":240},"blog\u002Fen\u002Fblog\u002Fconfiguration-nginx-avec-vuejs-erreur-404.md","Configuring Nginx with VueJS (404 error)",{"type":7,"value":8,"toc":217},"minimark",[9,14,18,24,28,38,42,45,50,53,56,60,63,116,119,206,209,213],[10,11,13],"h2",{"id":12},"real-world-use-case","Real-world use case",[15,16,17],"p",{},"Your page refreshes return a 404 error on a VueJS SPA behind Nginx: here's the configuration that fixes this behavior.",[15,19,20],{},[21,22,23],"strong",{},"As part of a web project running under Docker with Nginx, I ran into an issue with a VueJS project. The absolute path isn't known by the Nginx server due to normal VueJS behavior. A 404 error appears when refreshing a URI.",[10,25,27],{"id":26},"configuring-nginx-with-vuejs","Configuring Nginx with VueJS",[15,29,30,31,37],{},"When using an Nginx web server with VueJS, you may have run into the 404 error. As a reminder, Nginx is a web server and VueJS is a JavaScript framework. Once our web application is finished with VueJS, it needs to be transpiled and minified with webpack. At the end of this, we get a ready-to-use \"dist\" folder that the Nginx server will host and serve. However, we quickly notice a 404 error appearing when accessing our various URIs (e.g., ",[32,33,34],"a",{"href":34,"rel":35},"https:\u002F\u002Fkassianoff.fr\u002F**blog\u002F",[36],"nofollow","**).",[10,39,41],{"id":40},"our-environment","Our environment",[15,43,44],{},"In my case, I'm using the latest version of Nginx, 1.21.6, dated January 25, 2022. The server is published as a micro-service with a default configuration. The dist folder is placed in the web server's default location. I access my production server fine, but as soon as I refresh the page on a URI, I get a 404 web error.",[46,47,49],"h3",{"id":48},"vuejs","VueJS",[15,51,52],{},"During the development phase in VueJS, a router using \"hash\" mode by default is used, which poses no problem in the local (or online) environment, since it uses a hash character (#) before the URL passed internally. Since this part of the URL is never sent to the server, it doesn't require any special handling.",[15,54,55],{},"In production (behind a reverse proxy), the home page is accessible normally with no issues. However, when you refresh the page, a 404 error will appear, and the resource being accessed can't be found on the server, because the routing address defined by the Vue router is treated as the URL address, yet that address path doesn't exist, hence the 404 issue.",[46,57,59],{"id":58},"nginx","Nginx",[15,61,62],{},"Looking at my Nginx server's default configuration, there's no real \"menu\u002Findex\" resource under the \u002Fdist root directory in the Nginx configuration. These access resources are rendered in JS. The server's initial Nginx configuration is as follows (assuming the domain name is: kassianoff.fr):",[64,65,69],"pre",{"className":66,"code":67,"language":58,"meta":68,"style":68},"language-nginx shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","server\n{\n    listen 80;\n    server_name kassianoff.fr;\n    index index.php index.html index.htm default.php default.htm default.html;\n    root \u002Fvar\u002Fwww\u002Fkassianoff\u002Fdist;\n}\n","",[70,71,72,80,86,92,98,104,110],"code",{"__ignoreMap":68},[73,74,77],"span",{"class":75,"line":76},"line",1,[73,78,79],{},"server\n",[73,81,83],{"class":75,"line":82},2,[73,84,85],{},"{\n",[73,87,89],{"class":75,"line":88},3,[73,90,91],{},"    listen 80;\n",[73,93,95],{"class":75,"line":94},4,[73,96,97],{},"    server_name kassianoff.fr;\n",[73,99,101],{"class":75,"line":100},5,[73,102,103],{},"    index index.php index.html index.htm default.php default.htm default.html;\n",[73,105,107],{"class":75,"line":106},6,[73,108,109],{},"    root \u002Fvar\u002Fwww\u002Fkassianoff\u002Fdist;\n",[73,111,113],{"class":75,"line":112},7,[73,114,115],{},"}\n",[15,117,118],{},"The Vue router jump parameter therefore needs to be added to the server's Nginx configuration. The first page here is index.html. The correct configuration is as follows:",[64,120,122],{"className":66,"code":121,"language":58,"meta":68,"style":68},"server\n{\n    listen 80;\n    server_name kassianoff.fr;\n    index index.php index.html index.htm default.php default.htm default.html;\n    root \u002Fvar\u002Fwww\u002Fkassianoff\u002Fdist;\n\n    #Vue router configuration\n    location \u002F {\n        try_files $uri $uri\u002F @router;\n        index index.html;\n    }\n    location @router {\n        rewrite ^.*$ \u002Findex.html last;\n    }\n}\n",[70,123,124,128,132,136,140,144,148,154,160,166,172,178,184,190,196,201],{"__ignoreMap":68},[73,125,126],{"class":75,"line":76},[73,127,79],{},[73,129,130],{"class":75,"line":82},[73,131,85],{},[73,133,134],{"class":75,"line":88},[73,135,91],{},[73,137,138],{"class":75,"line":94},[73,139,97],{},[73,141,142],{"class":75,"line":100},[73,143,103],{},[73,145,146],{"class":75,"line":106},[73,147,109],{},[73,149,150],{"class":75,"line":112},[73,151,153],{"emptyLinePlaceholder":152},true,"\n",[73,155,157],{"class":75,"line":156},8,[73,158,159],{},"    #Vue router configuration\n",[73,161,163],{"class":75,"line":162},9,[73,164,165],{},"    location \u002F {\n",[73,167,169],{"class":75,"line":168},10,[73,170,171],{},"        try_files $uri $uri\u002F @router;\n",[73,173,175],{"class":75,"line":174},11,[73,176,177],{},"        index index.html;\n",[73,179,181],{"class":75,"line":180},12,[73,182,183],{},"    }\n",[73,185,187],{"class":75,"line":186},13,[73,188,189],{},"    location @router {\n",[73,191,193],{"class":75,"line":192},14,[73,194,195],{},"        rewrite ^.*$ \u002Findex.html last;\n",[73,197,199],{"class":75,"line":198},15,[73,200,183],{},[73,202,204],{"class":75,"line":203},16,[73,205,115],{},[15,207,208],{},"After restarting the Nginx server, the problem is gone. You'll notice the configuration is simple, since I don't use Nginx directly facing the web but behind a reverse proxy that handles TLS as well as routing with specific middlewares. If you run into any issue, feel free to share your feedback on the topic.",[10,210,212],{"id":211},"conclusion","Conclusion",[214,215,216],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":68,"searchDepth":82,"depth":82,"links":218},[219,220,221,225],{"id":12,"depth":82,"text":13},{"id":26,"depth":82,"text":27},{"id":40,"depth":82,"text":41,"children":222},[223,224],{"id":48,"depth":88,"text":49},{"id":58,"depth":88,"text":59},{"id":211,"depth":82,"text":212},[227],"Linux","\u002Fimages\u002Fblog\u002Fconfiguration-nginx-avec-vuejs-erreur-404\u002Fc5873a58c2.jpg",null,"2022-05-24","As part of a web project running under Docker with Nginx, I ran into an issue with a VueJS project.","md",{},"\u002Fen\u002Fblog\u002Fconfiguration-nginx-avec-vuejs-erreur-404",[236],"certificat-lpi-linux-essentials-exam-010-150",{"title":5,"description":231},"configuration-nginx-avec-vuejs-erreur-404","en\u002Fblog\u002Fconfiguration-nginx-avec-vuejs-erreur-404","dK6K_skcxmqkFd--UHYb1Pg63ZS_mR-SFJmWVF0xU4A",[242,251,259,266,272,279,287,294],{"title":243,"path":244,"stem":245,"date":246,"cover":247,"categories":248,"children":-1},"Microsoft: Security, Compliance, and Identity Fundamentals","\u002Fen\u002Fblog\u002Fcertificat-microsoft-security-compliance-and-identity-fundamentals","en\u002Fblog\u002Fcertificat-microsoft-security-compliance-and-identity-fundamentals","2022-10-17","\u002Fimages\u002Fblog\u002Fcertificat-microsoft-security-compliance-and-identity-fundamentals\u002F70ef935f2a.png",[249,250],"Certifications","Microsoft",{"title":252,"path":253,"stem":254,"date":255,"cover":256,"categories":257,"children":-1},"Hack In Paris 2022: the Cybersecurity Conference of the Year","\u002Fen\u002Fblog\u002Fhack-in-paris-2022-conference-cybersecurite-de-lannee","en\u002Fblog\u002Fhack-in-paris-2022-conference-cybersecurite-de-lannee","2022-06-19","\u002Fimages\u002Fblog\u002Fhack-in-paris-2022-conference-cybersecurite-de-lannee\u002F8ad6403cd6.png",[258],"Évènement",{"title":260,"path":261,"stem":262,"date":263,"cover":264,"categories":265,"children":-1},"Deploying Microsoft Defender ATP with Ansible","\u002Fen\u002Fblog\u002Fdeployer-microsoft-defender-atp-avec-ansible","en\u002Fblog\u002Fdeployer-microsoft-defender-atp-avec-ansible","2022-05-28","\u002Fimages\u002Fblog\u002Fdeployer-microsoft-defender-atp-avec-ansible\u002F20b08b6e35.jpg",[227],{"title":267,"path":268,"stem":269,"date":230,"cover":270,"categories":271,"children":-1},"Configuring Terraform for Scaleway\u002FOVHcloud","\u002Fen\u002Fblog\u002Fblog-configuration-terraform-scaleway-ovhcloud","en\u002Fblog\u002Fblog-configuration-terraform-scaleway-ovhcloud","\u002Fimages\u002Fblog\u002Fblog-configuration-terraform-scaleway-ovhcloud\u002Fce5716efc3.png",[227],{"title":273,"path":274,"stem":275,"date":276,"cover":277,"categories":278,"children":-1},"Manage Your IT Fleet with GLPI","\u002Fen\u002Fblog\u002Fcertificat-gerez-votre-parc-informatique-avec-glpi","en\u002Fblog\u002Fcertificat-gerez-votre-parc-informatique-avec-glpi","2022-01-12","\u002Fimages\u002Fblog\u002Fcertificat-gerez-votre-parc-informatique-avec-glpi\u002Fb3a9f2486c.jpg",[249],{"title":280,"path":281,"stem":282,"date":283,"cover":284,"categories":285,"children":-1},"Secure Your Infrastructures","\u002Fen\u002Fblog\u002Fcertificat-securisez-vos-infrastructures","en\u002Fblog\u002Fcertificat-securisez-vos-infrastructures","2021-10-29","\u002Fimages\u002Fblog\u002Fcertificat-securisez-vos-infrastructures\u002Fdcf3ea9308.jpg",[249,286],"Sécurité",{"title":288,"path":289,"stem":290,"date":291,"cover":292,"categories":293,"children":-1},"On the Road to Electric with Tesla","\u002Fen\u002Fblog\u002Fen-route-vers-electrique-avec-tesla","en\u002Fblog\u002Fen-route-vers-electrique-avec-tesla","2021-07-07","\u002Fimages\u002Fblog\u002Fen-route-vers-electrique-avec-tesla\u002F7dec469e8d.jpg",[258],{"title":295,"path":296,"stem":297,"date":298,"cover":299,"categories":300,"children":-1},"Stormshield Network Administrator","\u002Fen\u002Fblog\u002Fcertificat-stormshield-network-administrator","en\u002Fblog\u002Fcertificat-stormshield-network-administrator","2021-04-12","\u002Fimages\u002Fblog\u002Fcertificat-stormshield-network-administrator\u002Fca545c454a.png",[249,286],[302],{"id":303,"title":304,"body":305,"categories":356,"cover":357,"cover_contain":358,"credly_badge_id":359,"date":360,"description":361,"extension":232,"meta":362,"navigation":152,"path":363,"related_certifications":229,"seo":364,"slug":236,"stem":365,"__hash__":366},"blog\u002Fen\u002Fblog\u002Fcertificat-lpi-linux-essentials-exam-010-150.md","LPI Linux Essentials – Exam 010-150",{"type":7,"value":306,"toc":352},[307,309,312,315,344,347,349],[10,308,13],{"id":12},[15,310,311],{},"To make my work on Linux servers for clients more reliable, I took the LPI Linux Essentials certification.",[15,313,314],{},"The \"LPI Linux Essentials\" training path is designed for systems professionals.\nThe online course catalog covers the following modules:",[316,317,318,324,329,334,339],"ul",{},[319,320,321],"li",{},[21,322,323],{},"The Linux Community and a Career in Open Source",[319,325,326],{},[21,327,328],{},"Finding Your Way on a Linux System",[319,330,331],{},[21,332,333],{},"The Power of the Command Line",[319,335,336],{},[21,337,338],{},"The Linux Operating System",[319,340,341],{},[21,342,343],{},"Security and File Permissions",[15,345,346],{},"The training takes about 2 days.\nYou should then allow around 1h for the certification exam itself: LPI Linux Essentials.",[10,348,212],{"id":211},[15,350,351],{},"This training via NDG prepares for the LPI Linux Essentials certification, covering the basics of the Linux ecosystem: the command line, the operating system, and file security. It is a solid first certification for anyone starting out on Linux systems. An essential foundation before tackling more advanced Linux certifications.",{"title":68,"searchDepth":82,"depth":82,"links":353},[354,355],{"id":12,"depth":82,"text":13},{"id":211,"depth":82,"text":212},[249,227],"\u002Fimages\u002Fblog\u002Fcertificat-lpi-linux-essentials-exam-010-150\u002F4497f02702.jpg",false,"dbc3a0e7-5909-47df-b9b1-c05ecb60ccb4","2017-11-24","LPI Linux Essentials Exam 010-150 certification via an NDG online course.",{},"\u002Fen\u002Fblog\u002Fcertificat-lpi-linux-essentials-exam-010-150",{"title":304,"description":361},"en\u002Fblog\u002Fcertificat-lpi-linux-essentials-exam-010-150","xeSCYjFx9jOx2b6qHUfhxCIjiKjTseXAXAtG-rh6Z1M",1786644883870]