This blog is my first fully self hosted website. I had to learn many things that I initially did not want to learn. I just wanted to code a cool blog, to document and collect code snippets and solutions to problems that occur during developing websites. But to reach my goal of having a fully functional Next.js/Node.js website online, I had to solve all issues that came across. So I learned how to set up a Nginx server, connect it to a domain, add SSL encryption and how to build a release pipeline via GitHub. It all went surprisingly smooth until I implemented my first post request for the login. It did not go through, because the CORS preflight failed.
No big deal, I thought. I knew how to set up the Node.js Express server, to solve this issue. Just install the CORS Npm-package and configure it!
$ npm i cors
And within the server.js file, add:
app.use(cors());
Maybe be a bit more fancy, and add the URL, from where the fetch request is made:
app.use(cors({ origin: "https://besure.blog", }));
Still, it did not work. So I tried all the solutions that Google would provide and ended up with something like:
app.use(cors({ origin: ["http://localhost:3000", "https://besure.blog"], allowedHeaders: ['Content-Type'], methods: ['OPTIONS', 'GET', 'POST', 'PATCH', 'DELETE'] }));
But still, no success. Simultaneously, I also changed all possible parameters of the frontend fetch request. In the end, it looked something like this:
fetch(fetchUrl, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8' }, body: JSON.stringify(user) })
You notice the variable fetchUrl here. And this part is crucial to the final solution, which I discovered after a whole month of working on this issue.
In local development, a fetch request will be made to http://localhost:6000, or what ever the port of your backend is. In production however, there might by port forwarding by the reverse proxy or any other difference, that will make your life hard. A friend gave me the advice, to just remove the address (localhost:6000) for the live website and to just keep what follows. In my case that was "/users/login". Because I want to be able to send fetch requests in local development AND in production, in local development, the URL should be like this:
http://localhost:6000/users/login
And in production, it is like this:
/users/login
To make this work, I just added two environmental variables to my Next.js frontend (.env.local file):
NEXT_PUBLIC_URL=http://localhost:6000 NEXT_PUBLIC_ISLOCAL=true
And to create the fetchUrl in the code:
const fetchUrl = `${process.env.NEXT_PUBLIC_ISLOCAL == "true" ? process.env.NEXT_PUBLIC_URL : ""}/users/login`;
Note: In production, there is no ISLOCAL variable, so the second option will be taken, an empty string. In development, the string value "true" is exists for ISLOCAL and therefore, the URL string is used.
Now we arrive at the most important part and the final solution to my "CORS" issue: The nginx configuration! It is crucial that the frontend fetch request will be forwarded to the correct URL AND port of the backend. Since our backend is running on port 6000, this is the code of the config file in /etc/nginx/sites-available/website.de:
server { ... location /users { proxy_pass http://localhost:6000; proxy_set_header Host $host; } ... }
And combining all these code parts, it finally worked for me! Maybe it will help someone else too.