<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ servers - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ servers - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 18 May 2026 10:47:59 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/servers/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Linux Server Tutorial – How to Login, Communicate, and Transfer Files ]]>
                </title>
                <description>
                    <![CDATA[ Did you know that 96% of the top 1 million web servers are running Linux?  Yes. You heard that right. So being able to work with Linux servers is a great skill to have. In this article, you'll learn how to connect to a Linux server using SSH, how ]]>
                </description>
                <link>https://www.freecodecamp.org/news/linux-server-tutorial/</link>
                <guid isPermaLink="false">66ba10eba1a94b68c6ae94b6</guid>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Arunachalam B ]]>
                </dc:creator>
                <pubDate>Tue, 28 Feb 2023 23:55:22 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/02/Server-Commands---Brief-2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Did you know that <a target="_blank" href="https://www.zdnet.com/home-and-office/networking/can-the-internet-exist-without-linux/">96% of the top 1 million web servers</a> are running Linux? </p>
<p>Yes. You heard that right. So being able to work with Linux servers is a great skill to have.</p>
<p>In this article, you'll learn how to connect to a Linux server using SSH, how to communicate with other users on the server, and you'll see a handy file transfer mechanism. </p>
<p>A quick note before we dive in: the IP address that I've given in the example commands and the one in the example screenshots will vary. The reason is, I don't have a server to demonstrate everything, so I made my laptop a server. So, all my example screenshots will be displaying my local IP addresses beginning with <code>192.168...</code>.</p>
<h2 id="heading-how-to-log-in-to-a-server-running-linux">How to Log in to a Server Running Linux</h2>
<p>I love to develop software, but I really dislike DevOps and deployments. When I have some kind of DevOps work, I'll hand it over to the specialist in my team and stay away. </p>
<p>The reason is my lack of experience in handling servers. But sometimes, when my team members are not available, I'm forced to do deployments. </p>
<p>So, the initial step for a (manual) deployment is to log in to the server. To log in, you need to know the IP address and the password of the server.</p>
<p>Above all, you need to have the SSH client installed on your machine. This comes pre-installed on almost all Linux distros.</p>
<p>If you don't have it installed, you can install it by running the below command in the terminal:</p>
<pre><code class="lang-bash">sudo apt install openssh-client
</code></pre>
<p>In order to access the server via SSH, the server should have <code>SSH Server</code> installed and the service running in it. This will be pre-installed on almost all Linux servers.</p>
<p>Let's connect to the server now.</p>
<p>You need the following items to login to the server:</p>
<ol>
<li>IP Address of the server machine</li>
<li>Username of the server</li>
<li>The password of the user</li>
</ol>
<pre><code class="lang-bash">ssh user@&lt;ipaddress&gt;
</code></pre>
<p>Here's an example command:</p>
<pre><code>ssh ubuntu@<span class="hljs-number">45.244</span><span class="hljs-number">.96</span><span class="hljs-number">.73</span>
</code></pre><p>The above command will ask for the password. If you enter the password, it will log in to the server.</p>
<p><img src="https://lh3.googleusercontent.com/HgByhOjGdN5KCqnk-0RXRAqvrHXdttXbpxjatpEOm6f7k7_1VAEUZKu7xVZ6CrLqpeG6vAu2Lj3zj7sSFpUPF-7TEFRJ3FIeMOXAQyMyPXK2DWYSf2zPRM9onI1U-ieT9zRNee8blRaysH4LgE3ctFc" alt="Image" width="600" height="400" loading="lazy">
<em>Sample output of connect with Linux Server using SSH Command.</em></p>
<p>Alternatively, you can log in without any prompt by adding the <code>-p</code> option with the <code>sshpass</code> command prepended to <code>ssh</code> command. You need to have <code>sshpass</code> installed to try this method. </p>
<p>The syntax looks like this:</p>
<pre><code class="lang-bash">sshpass -p &lt;password&gt; ssh user@&lt;ipadrress&gt;
</code></pre>
<p>And here's a quick example:</p>
<pre><code class="lang-bash">sshpass -p password ssh user@45.244.96.73
</code></pre>
<p>This is not the recommended way. Command line arguments are visible to all users (for example, <code>ps -ef | grep sshpass</code>). <code>sshpass</code> attempts to hide the argument, but there is still a window during which <strong>all users</strong> can see your password passed by argument. </p>
<p>There's also a command in Linux called <code>history</code> which displays your past commands. Any user who gain access to your machine may run the <code>history</code> command and find your server login credentials. But if you've entered the password in a prompt, it'll not be shown to users in the <code>history</code> command. </p>
<p>Here's the output of <code>history</code> command:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/image-265.png" alt="Image" width="600" height="400" loading="lazy">
<em><code>history</code> command displays past commands</em></p>
<h2 id="heading-how-to-log-in-to-a-particular-user-in-the-linux-terminal">How to Log in to a Particular User in the Linux Terminal</h2>
<p>In Linux, we can log in to different users using two different approaches:</p>
<ol>
<li>ssh</li>
<li>login</li>
</ol>
<h3 id="heading-how-to-log-in-with-ssh">How to log in with SSH</h3>
<p>As already discussed with the SSH command, we can login to another user using the same syntax.</p>
<pre><code class="lang-bash">ssh &lt;user&gt;@&lt;ipaddress&gt;
ssh root@45.244.96.73
</code></pre>
<h3 id="heading-how-to-log-in-with-the-login-command">How to log in with the <code>login</code> command</h3>
<p>You can use the <code>login</code> command to switch the user inside the server.</p>
<p>Let's assume you've logged into the <code>ubuntu</code> user in the <code>45.244.96.73</code> server. Later you found that you want to switch to the <code>ak</code> user to perform some admin operations. In such a case, you can quickly switch the user using the <code>login</code> command.</p>
<p>Syntax for the <code>login</code> command is:</p>
<pre><code class="lang-bash">sudo login &lt;username&gt;
</code></pre>
<p>And here's an example to switch to the root user:</p>
<pre><code>sudo login ak
</code></pre><p>The above command will prompt for the password similar to login via SSH.</p>
<p><img src="https://lh6.googleusercontent.com/NdnozHMxq8yjUemRfgXbLseAJJOtdjFuogF80P3sXiHA9WsH-kaM7pfsqq7u9tMrCgB1kIU8up5_stEPbov2w7SEI8Tx-0jWfSDwGn4xMX0U0CZr_cG3GymiYQTQ3FIZNLGCRZDLzOwvyrvy1cWGq9I" alt="Image" width="600" height="400" loading="lazy">
<em>Sample output to connect with Local User</em></p>
<p>Hurray, we successfully logged in to another user. Now let’s explore how to communicate with these sessions.</p>
<p>A quick note before jumping onto the communication section. If you wish to log out of the logged-in user, you can simply run the <code>logout</code> command.</p>
<h2 id="heading-how-to-communicate-between-sessions-in-linux">How to Communicate Between Sessions in Linux</h2>
<p>Did you know that you can use your Linux terminal as a chat interface?</p>
<p>Well, yes – you can. If you and your colleagues are connected as their own users to the same server using SSH, then you all can communicate via the terminal.</p>
<p>As with all such features, there are a few pre-requisites to continue.</p>
<p>First, make sure that message access is enabled in the receiver system. To verify that, type the following command in the terminal:</p>
<pre><code class="lang-bash">mesg
</code></pre>
<p><img src="https://lh3.googleusercontent.com/GhfC5V8FTtbZMrFpMef_tdVanRwDZv7qksWLAtSp2Oi6LixFWOLUme82szxja60ho6h04jHi5cUjkFTAgKepTKw3KsRjr7Yx1yt1bJ_rvCWr1CFDqX9L-rfASEuSHWD4Emx7BdBKAyLqGcl3omuRsYg" alt="Image" width="600" height="400" loading="lazy">
<em>Terminal command to verify if message access is enabled</em></p>
<p>The response will be either yes or no.</p>
<p><code>is y</code> – Message access enabled</p>
<p><code>is n</code> – Message access disabled</p>
<p>To toggle this feature, you have to pass the symbol along with the <code>mesg</code> command.</p>
<pre><code class="lang-bash">mesg y      <span class="hljs-comment"># Enable message access</span>
mesg n      <span class="hljs-comment"># Disable message access</span>
</code></pre>
<p>Let's try to communicate with others.</p>
<p>There are two commands available to communicate:</p>
<ol>
<li><code>write</code></li>
<li><code>wall</code></li>
</ol>
<p>The <code>write</code> and the <code>wall</code> commands use a fairly simple mechanism. Both commands take a message from one session and deliver it to other session(s).</p>
<h3 id="heading-how-to-use-the-write-command">How to use the <code>write</code> command</h3>
<p>You can use the <code>write</code> command to message a single user (Direct Message).</p>
<pre><code class="lang-bash">write &lt;username&gt;
</code></pre>
<pre><code>write ak
</code></pre><p>After entering this command, it prompts for the input message to send. We can send any number of messages using the <code>write</code> command.</p>
<p>Here's an example showing the communication between the users <code>ak</code> and <code>gogosoon</code>:</p>
<p><img src="https://lh5.googleusercontent.com/j5imQ3k0CpoU80sgoesN-0-C2YE94Q130NeWqCaWx9jU42jEhKKR31Izy1H5k6WSb5TRd5pVUfa4CPg0GUeY_qnIzcoapWEK8D6W42JqMIVrFe7vlBqVWRvbmqcTvYIKvRw4nUUTG59zlZ_MRcP3s7o" alt="Image" width="600" height="400" loading="lazy">
<em>Sample output of sending message to the user</em></p>
<p><img src="https://lh6.googleusercontent.com/DXtSsy3TbbeHr8oX3bB8mSxPxkXy6zsVjJ9WwlI8HfXJJVi6RL8BgTHaSUivl1rZzVpD9aEq6LPn9Gfq8_8FHTzwXE2FGtUz4TTO46b5GU8NXYCu-8gY-l4k0V328Sj0CHe6OMuay7SxdGlkf5LTLdg" alt="Image" width="600" height="400" loading="lazy">
<em>Sample output of received message using write command</em></p>
<p>Let's turn off messaging on the <code>ak</code> user and try to send a message from the <code>gogosoon</code> user.</p>
<pre><code class="lang-bash">mesg n
</code></pre>
<p><img src="https://lh3.googleusercontent.com/Nyt9Ror-xWpTtnGd77lkjXM_cb7hUcj1fE3fbJEk-kEc-EPsUxQCwhpuo_br1ba44XEdnGa9WzDQkE2OcY4lZW9R7LFCob4kNVYBPmm65GNhpfK-UQ198i73dWynZ4pfeX4kzOB3oWwE0-HzLeNA9Ss" alt="Image" width="600" height="400" loading="lazy">
<em>Turn off messaging for <code>ak</code> user</em></p>
<p><img src="https://lh4.googleusercontent.com/QY3yKIRAMJpxHOlaY3jJfyuQI5zLDyB8iecBIkAIzeuAHobYU1q6IiWDyN9sVu1Xikl5psyLMtpy7TFYAxohNbxINlQFS-5zE7Y18TSbYlVR6EhvCDWCBfWR3XYVL2CheGvny3CsTLCkiq3_f9F8hjY" alt="Image" width="600" height="400" loading="lazy">
<em>Trying to send a message to <code>ak</code> user from <code>gogosoon</code> user</em></p>
<h3 id="heading-how-to-use-the-wall-command">How to use the <code>wall</code> command</h3>
<p>You can use the <code>wall</code> command to write messages to all logged-in users. This command will display the message or the contents of a file to all the logged-in users. Basically, it will broadcast the message to all the sessions.</p>
<pre><code class="lang-bash">wall &lt;message&gt;
</code></pre>
<p><img src="https://lh3.googleusercontent.com/9emd9tiz1MkQHQDYaCs_2Y8Cc87T1Yt3GLKsYPGZ9IYEZGecDE95zaLZJdpNik5szyKB1_Y-d60WsOIlZPVGy1YrNWmfb2tbCQltO3e6fxxXX1npqGnmaVnuQvqOsaz18h3b8Q25GUpHnzVtaBiWQwI" alt="Image" width="600" height="400" loading="lazy">
<em>Sample command to send message to all logged in user</em></p>
<p><img src="https://lh5.googleusercontent.com/ey4zUh9Rv_OEZcn3LUn4ksRg_eMekUUNyOvvny7_E0PGvbODYjkYGcZ7fsBoZ7W3DwZQOshbCJlhm7AQ6_ty5rCItysHSAU_ceGvLmuUFimchZe8DQRtx8R9MK_CDfmOz1an14Qd6ScB3YHcYVrMY5Q" alt="Image" width="600" height="400" loading="lazy">
<em>Sample output of received message from user <code>gogosoon</code></em></p>
<p>Here we can see the message in the user <code>ak</code>. Similarly, it prompts the message to every logged-in user.</p>
<h2 id="heading-how-to-transfer-files-tofrom-the-server-using-the-linux-terminal">How to Transfer Files to/from the Server Using the Linux Terminal</h2>
<p>I believe most people would recommend using FTP to transfer files from and to the server. FTP provides more control over the files such as the ability to rename, delete, move, and modify files from the remote computer. </p>
<p>But FTP does not offer protection against anyone who may be trying to view your network credentials. </p>
<p>We can do this using a Linux terminal – and believe me it's super simple.</p>
<p>You can transfer files using the <code>scp</code> command. </p>
<p><strong>SCP</strong> stands for Secure Copy Protocol. This command allow the user to share the files in a secure way. Unlike FTP, SCP is highly secure. It uses secure shell to encrypt both your data and credentials. SCP does not provide facilities to control files. </p>
<p>Since this command follows the end-to-end encryption protocol, it uses encryption over an <strong>SSH connection.</strong> This will protect the files from suspicious attacks. </p>
<p>Transferring files via SCP will be slow when compared with FTP. But, it's a better alternative to FTP if you need a one-time movement of files. </p>
<p>The syntax for the SCP command looks like this:</p>
<pre><code class="lang-bash">scp [OPTIONS] &lt;user&gt;@&lt;src_host&gt;:&lt;file_src_path&gt; &lt;user&gt;@&lt;dest_host&gt;:&lt;file_dest_path&gt;
</code></pre>
<p>Using the SCP Command we can do the following operations,</p>
<ol>
<li>Copy the file from our machine to the server machine</li>
<li>Copy the file from the server machine to our machine</li>
<li>Copy the file from one server to another server</li>
</ol>
<p>Let's look at each of these operations in more detail now.</p>
<h3 id="heading-how-to-copy-the-file-from-the-client-machine-to-the-server-machine">How to copy the file from the client machine to the server machine</h3>
<p>When copying files from the local to the server machine, you should enable SSH in the server. This is because SCP uses SSH to establish the connection between the client and server machines.</p>
<pre><code>scp &lt;filepath&gt; user@hostname:<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">filepath</span>&gt;</span></span>
</code></pre><pre><code class="lang-bash">scp sample.txt ak@45.244.96.73:/home/ak/
</code></pre>
<p><img src="https://lh6.googleusercontent.com/xRevVCaYkcjReW_KL5z3AR4URLrtfj7ddjb5syy9XYAM77ndXOwPsq5aqGpEY3Y6Py3ro2kYaklDYOjqSkf4t_eFDwsTvlHn7-EyI8GW1UXKSMtb17kThqdrgDZTLpjIrr2Q9b82EJqJbADsm5IAmRY" alt="Image" width="600" height="400" loading="lazy">
<em>Sample Output to transfer files from Local machine to Server</em></p>
<p><img src="https://lh5.googleusercontent.com/R1Seq1__aIY64McpCrOxq2EHDzIUbkH9Yp_hjaGdTBjMx_Ig1Dl4JImNmeUJ83rKQnXbgaMOeqkgNwG7NAFEF7uHFPFzsmmGcrxXeAKYEbZSptDmPY5DI-YN7shaLmESQNwan8rwaVXI--TffliTR-M" alt="Image" width="600" height="400" loading="lazy">
<em>File uploading status</em></p>
<p><img src="https://lh5.googleusercontent.com/Qn41hy9eTLlH8qHHdBNDHDP-svOKwuyEJI_1xcpQd3dMBepILQA6hu_yEA-fj-Yaguq6hoqYOqR5GunCfmnsAosKGosovwjKdXAFuLODjUXV4GwtLPUDQy9a69oLP4pIUkcoKnj2doetcfDQUwBiNDg" alt="Image" width="600" height="400" loading="lazy">
<em><code>Sample.txt</code> has been transferred to server</em></p>
<p> The above screenshots show that the file was copied to the server.</p>
<h3 id="heading-how-to-copy-the-file-from-the-server-machine-to-the-client-machine">How to copy the file from the server machine to the client machine</h3>
<p>Let's see how to transfer the file from the server to our local machine:</p>
<pre><code class="lang-bash">scp server_username@&lt;server_host&gt;:&lt;filepath&gt; &lt;local_path&gt;
</code></pre>
<p>This command will copy the file <code>server_file.txt</code> from the server machine to the local machine in the test directory.</p>
<pre><code class="lang-bash">scp ak@45.244.96.73:/home/ak/server_file.txt /home/gogosoon/<span class="hljs-built_in">test</span>
</code></pre>
<p>If you leave the local_path empty, the file will be copied to the home directory.</p>
<p><img src="https://lh3.googleusercontent.com/oDuUIGXLgFnUYwZY1qrym9lsy8rvLI-cktIOMA-0eVASMw3c1NofUApRERSH8jAEt8Jqv8KTML7xHT_oQihD6cECczYyeTI0MyVlrixpR0CJhl6oMbmWqGuvuyMa9yXV2esY0XovawvN47Y5xV9_iG4" alt="Image" width="600" height="400" loading="lazy">
<em>Create file in server to transfer</em></p>
<p><img src="https://lh4.googleusercontent.com/8UQdbKsHlapYP3pIKrlYM5_qAdVGBxN5I_cr_u3ugIyiHuzTXjHSjWsGMKESFascLc8KvqH_aAglXfB2qSiW2-90t3ceWVOQ-PYs-tkym20dakTq7cilKHe0_CabuBr8ufueVqmam1CnfTEQKL7dSDo" alt="Image" width="600" height="400" loading="lazy">
<em>Sample output of transfer file from server to local</em></p>
<h3 id="heading-how-to-copy-the-file-from-one-server-to-another-server">How to copy the file from one server to another server</h3>
<p>Let's assume you're taking a backup of a file from a server. You don't want this file to be stored on your machine due to some security concerns. But, you want to transfer this file to another server.</p>
<p>Copying files from one server to another server is also possible with the <code>scp</code> command:</p>
<pre><code class="lang-bash">scp &lt;src_user&gt;@&lt;src_host&gt;:&lt;src_path&gt; &lt;dest_user&gt;@&lt;dest_host&gt;.com:/&lt;dest_path&gt;
</code></pre>
<pre><code class="lang-bash">scp ak@45.244.96.73:/home/ak/script.sh gogosoon@45.244.196.173:/home/gogosoon/
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Alright, we've come to the end of this tutorial. I hope you all enjoyed learning about these helpful commands. </p>
<p>If you are a DevOps Engineer, Linux Developer, or you're learning Linux, these commands will be very useful. If you enjoyed this guide, please share it with your colleagues/friends who are more into working on servers. </p>
<p>To learn more about Linux, subscribe to my email newsletter on my <a target="_blank" href="https://5minslearn.gogosoon.com/?ref=fcc_server_guide">site</a> and follow me on social media. </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How Modern Compute Platforms Work ]]>
                </title>
                <description>
                    <![CDATA[ The way things sit now, if you were somehow allergic to computers, you'd be hard pressed to really banish them from your life, no matter where you found yourself.  Taking a quiet walk in the woods? What about the smartphone in your pocket. Left the p... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/modern-compute-platforms/</link>
                <guid isPermaLink="false">66b99613d9d170feecefbba1</guid>
                
                    <category>
                        <![CDATA[ Cloud Computing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ David Clinton ]]>
                </dc:creator>
                <pubDate>Tue, 07 Feb 2023 20:22:32 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/02/pexels-panumas-nikhomkhai-1148820--1-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The way things sit now, if you were somehow allergic to computers, you'd be hard pressed to really banish them from your life, no matter where you found yourself. </p>
<p>Taking a quiet walk in the woods? What about the smartphone in your pocket. Left the phone in the car? See that cell phone tower just behind those trees? The odds are good that the tower is more than just an antenna. It could also be hosting an edge computing server. And don't think that there weren't computers embedded into the under-the-hood workings of the car (or bus) that drove you over.</p>
<p>Allergies aside, if you want to fully grasp the current state of the compute world, it'll be helpful to understand all the places computers can pop up and what they might look like. </p>
<p>In this article, we'll enumerate the classes into which modern compute devices can fall, and describe their strengths, weaknesses, and potential.</p>
<p>This is a chapter taken from the book, <a target="_blank" href="https://amzn.to/3FXXAfb">Keeping Up: Backgrounders to All the Big Technology Trends You Can't Afford to Ignore</a>. If you'd prefer to watch this chapter as a video, feel free to follow along here:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/0IW2GCTkCjI" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h1 id="heading-what-is-a-server">What is a Server?</h1>
<p>Honestly, I'd been working as a professional system administrator for a while before I could have properly answered that question. </p>
<p>The truth is that every server is a computer, and any computer can be a server. The term <em>server</em> simply implies that the device is providing some <em>service</em> to at least one external device (known as a <em>client</em>).</p>
<p>If the printer that's plugged into your desktop computer can be shared by the other computers in your local network, then your desktop is a server (a <em>printer server</em>, to be precise). </p>
<p>The WiFi router provided by your internet service provider is, by all definitions, a <em>network server</em> – as it <em>serves</em> network access to its clients. </p>
<p>And the tiny, $5 Raspberry Pi Zero single board device that powers your homemade surveillance camera is a <em>video server</em> – although that won't work without attaching a $7 camera module.</p>
<p>But that's not what most people are thinking about when they use the term. The first time I ever walked into the server room belonging to a mid-sized business, I was hit by the sound of dozens of powerful chassis fans and the heat from hard-working CPUs and fast-spinning disk drives. Instantly, I knew how folks normally use the word. </p>
<p>I also soon discovered that the heat was a problem: the admins were struggling to keep their server room properly cooled and would, over time, end up having to write off some expensive hardware due to heat-related failures.</p>
<p>So, by "server," we usually mean computers installed within those rack-mounted, stackable cases built to efficiently house and protect highly performant, expensive, and delicate components. </p>
<p>Server racks will normally live in well vented and cooled rooms with easy access to ample electrical power. You may have to search for them, but such rooms will also always contain colorful bundles of cabling, connecting the servers to networks.</p>
<p>As a rule, servers won't usually have displays or even keyboards plugged in, as they're likely to be managed remotely or, even more likely, fully automated and requiring no administration at all. </p>
<p>Server farms belonging to giant cloud providers like Amazon Web Services will have many thousands of commodity computers running within aisle after aisle of vast warehouses. When one fails, a monitoring panel somewhere will light up and a technician will eventually be dispatched to remove the server, throw it out, and slide a replacement into the newly-available rack.</p>
<p>No tears are shed when we say goodbye to hardworking and devoted old hardware in those places.</p>
<h1 id="heading-what-is-linux">What is Linux?</h1>
<p>Speaking of servers, did I mention that they all need some kind of operating system installed? </p>
<p>And did I mention further that the vast majority of the servers powering the vast majority of the operations that make the internet and all its functionality possible are running the Linux operating system? Oh, and did you know that the open source Linux operating system is available for free?</p>
<p>I didn't mention all that? My bad.</p>
<p>Well, servers need operating systems. Most servers (well over 90 percent of the virtual machine instances running on Amazon's AWS EC2, for instance) run Linux. And Linux is, indeed, freely available for any use on any server, laptop, desktop, router, embedded system, or supercomputer. </p>
<p>In fact every last one of the world's top 100 supercomputers uses Linux. And the Android smartphone OS? Yup. It's Linux, too.</p>
<p>Strictly speaking, "Linux" is the software kernel that allows a computer user to take control of a computer's physical hardware elements. The kernel translates your keystrokes into a format that will be understood by the drivers controlling your storage drives, memory, network interfaces, displays, and – in fact – keyboard and mouse. </p>
<p>Many thousands of additional software programs are closely associated with Linux, but they're actually part of the user space that hovers "above" the Linux kernel.</p>
<p>Having said all that, Linux, including its broader software ecosystem, dominates the server computing market right now. The fact that you can freely install and fire up as many physical or virtual instances as you like makes Linux very attractive, especially in the scripted workload orchestration world. </p>
<p>Virtualized Linux instances will often be brought to life and then, after completing a task that takes even a few seconds, killed off again. </p>
<p>The versatility and flexibility Linux brings to computing have been the spark of some deeply impressive innovation and creativity.</p>
<p>Part of the Linux versatility is the fact that you can choose from among hundreds of variations (known as <em>distributions</em>). </p>
<p>Are you looking to run enterprise-supported servers? Internet of Things (IoT) devices? Security testing machines? Multimedia management? Video or audio production? All of the above? None of the above? There's bound to be a distribution that's a good match for you. </p>
<p>And if the exact specs you need can't be found, feel free to rewrite the kernel itself and create your own distro.</p>
<p>Full disclosure: I know a thing or three about Linux, being the author of Linux in Action (Manning), a coauthor of Ubuntu Bible (Wiley), and the author of the Linux Fundamentals learning path at Pluralsight.</p>
<p>Fuller disclosure: I'm writing this on an Ubuntu Linux workstation in my home, where all of our many devices have been Linux-powered for more than a decade.</p>
<h1 id="heading-what-is-virtualization">What is Virtualization?</h1>
<p>We've already discussed virtualization in some depth as part of Chapter 3 (Understanding the Cloud), so we'll just cover some big-picture conceptual basics here.</p>
<p>In the old days, you'd come up with an idea for a new compute project and submit a proposal with your managers asking for money. When the project was green lighted, you'd estimate your requirements, solicit bids from hardware vendors, order a new server and, when it finally arrived, load it up with your application software. Then you'd fire it up, and let the world see what you'd done. </p>
<p>That's the way things usually worked: One project. One server. Lots of waiting time.</p>
<p>But what if you overestimated your compute needs by 50 percent? That'd be a few thousand dollars down the drain. And if an important but lightweight project didn't really need a full, standalone server, you'd often have to buy it anyway. </p>
<p>How about if the project would only have to run for a few months? Spend the money and hope you'll find a new use for the thing once your initial project shut down.</p>
<p>Awkward. Mountains of awkward.</p>
<p>Virtualization is a (mostly) software trick that lets you fool multiple installed operating systems into thinking they're all alone on a physical computer when they're really sharing it with other OS's. You can provision and run a single virtualization <em>host</em> of one flavor or another and then fill it up with one or a hundred virtual servers.</p>
<p>One of those servers might need a lot of system memory but only a GB or two of storage space. Another one might be heavy on video conversion tasks and storage but is only needed for a half an hour a day. A third could be a 24/7 monitoring system that just needs a quiet place to do its thing without anyone bothering it. </p>
<p>As long as you never push the physical host past its overall resource limits, the virtual machines can all coexist happily together. And when one service is no longer needed, you can reassign its freed-up resources to something else.</p>
<p>The ramp-ups and ramp-downs of a typical virtual server's life cycle are fast. For all intents and purposes, they'll generally launch and shut down instantly. </p>
<p>This is possible because the underlying hardware is always running – and because the OS image is small and, usually, optimized for virtual environments.</p>
<p>As <a target="_blank" href="https://www.freecodecamp.org/news/what-is-cloud-computing-beginners-guide/">we saw back in Chapter 3</a>, cloud-hosted services are all virtualized. As more and more IT infrastructure moves to the cloud, more and more of your online activities will be driven by virtual machines. </p>
<p>You won't notice the difference, but every time you search the internet or authenticate to an online account, there's a good chance that it's a container or VM you're connecting to, and not directly to a physical machine.</p>
<h1 id="heading-cloud-computing-terminology">Cloud Computing Terminology</h1>
<p>Like virtualization, we also talked about the cloud back in chapter 3. We mentioned how the public cloud market was dominated by AWS and, to a lesser extent, by Microsoft's Azure. </p>
<p>I'll just take a minute or two here to add a quick guide through some of the cloud industry's worst jargon.</p>
<p><strong>Infrastructure as a Service (IaaS)</strong> environments give you full access to virtual server instances. The provider will ensure the underlying hardware, networking, and security elements are in place and functioning, while it's your job to manage the OS and other software running on your instance. </p>
<p>Major IaaS players include Amazon's Elastic Compute Cloud (EC2) and Azure's Compute.</p>
<p><strong>Platform as a Service (PaaS)</strong> environments hide most or all of the infrastructure administration tasks from you, leaving you with an interface where you can run your own data or code. </p>
<p>One good example is AWS Elastic Beanstalk, which lets you upload your application code from where it'll be automatically deployed to Amazon's cloud. Other providers in this space include Heroku and Salesforce Lightning Platform.</p>
<p><strong>Software as a Service (SaaS)</strong> environments expose only an end-user interface, managing all layers of the administration infrastructure invisibly. </p>
<p>Microsoft's Office 365 and Google's G Suite are widely used SaaS office productivity tools. </p>
<p>But there's a growing marketplace of SaaS tools providing online software equivalents to many applications that, in years past, could only be used on standalone workstations. Those applications include accounting, computer assisted design (CAD), and graphic design solutions.</p>
<p><strong>Consumption-based pricing</strong> or, as it's sometimes known, pay-as-you-go billing, is a cornerstone of the cloud concept. The idea is that you don't have to gamble by investing up-front in infrastructure, but you can pay incremental amounts for units of compute services as you use them.</p>
<p>It might not always come out cheaper in the long-run. But pay-as-you-go definitely makes it easy to test application stacks and experiment with multiple alternative configurations before pulling the trigger on a full deployment. </p>
<p>It also means that – assuming you don't make any dumb configuration mistakes – it's nearly impossible to badly over-provision.</p>
<p><strong>On-demand</strong> is also sometimes referred to as self-service. The ability to request instant delivery of compute resources any time of the day/week/year gives you complete control over your organization's application life cycles. You're never at the mercy of other people's schedules and limitations.</p>
<p><strong>Service Level Agreements (SLAs)</strong> are legal disclosures published by companies in the business of providing services. </p>
<p>Even if the standard of resource reliability provided by the major public cloud platforms is generally excellent, accidents will happen. When you pay hourly or monthly fees for cloud services, the company's SLA tells you that you should anticipate downtime of a certain number of minutes or hours each month. </p>
<p>As an example, Amazon's SLA sets its EC2 availability rate at 99.99% each month. If, in a particular month, you encounter greater downtime, you might be eligible for service credits or refunds.</p>
<p><strong>Multitenancy</strong> is the placement of virtual instances belonging to multiple cloud customer accounts on a single hardware resource. </p>
<p>A multitenancy setup for a server instance will probably be significantly less expensive than a dedicated instance. Choosing a dedicated instance, however, would guaranty that your instance will never be hosted on a physical server alongside an instance from a second account. Security or regulatory considerations might require that you avoid multitenancy.</p>
<p><strong>Migration</strong> describes the process involved with moving existing business application and database workloads from local (on-premises) deployments to a cloud provider. Providers often make specialized tools and free tech support available for migrations.</p>
<p><strong>Elasticity</strong> describes the ways virtualized cloud resources can be quickly added to meet growing demand or, equally quickly, reduced in response to dropping demand. </p>
<p>Elastic resources are especially well-suited to maintaining application availability and health without incurring unnecessary costs. Elasticity can usually be automated, so applications will respond instantly to changing environments without the need for manual intervention.</p>
<h1 id="heading-what-is-serverless-computing">What is "Serverless" Computing?</h1>
<p>Serverless computing is no different from server computing. It's just that, even if you squint your eyes real tight, you don't get to see the server. </p>
<p>Or, to put it another way, serverless computing is like running a virtual server instance, but without having to configure its instance settings or log in to set things up.</p>
<p>In other words, you can't run software code of any kind without a computer somewhere processing your commands. So let's say that serverless is a form of virtualization where everything except your application code is abstracted. </p>
<p>In that sense, serverless platforms like Amazon's Lambda and Azure's Functions are a lot like the model used by Amazon Elastic Beanstalk. But they're so simple to use that they can easily be incorporated into a larger, highly automated multi-tier workload.</p>
<h1 id="heading-what-is-edge-computing">What is Edge Computing?</h1>
<p>Latency is the term we use to describe the time it takes for data to travel from a remote server across a network to your computer – or back in the other direction. Assuming you prefer fast service over slow service (which seems a safe assumption), high latency numbers are a bad thing.</p>
<p>Network engineers can invoke various magical spells – Oops! I mean clever configuration efficiencies – to reduce delays due to latency. </p>
<p>But no matter how many tricks they're hiding in their mysterious black bags, they can't ignore the laws of physics. Even using the best connections and configuration profiles, data still has to physically move across the distances between remote locations.</p>
<p>The only way to reduce this kind of latency is to shorten the distance. I suppose that one way would be for online service providers to very politely ask their customers to sell their houses and move somewhere closer to the servers running in the office (as if real estate prices weren't already high enough in Silicon Valley). </p>
<p>Alternatively, though, how about moving the server closer to the customer?</p>
<p>Ah. You've discovered edge computing: the fine art of installing large distributed networks of smaller servers where mirror copies of server data can be stored and, when necessary, fed to any customers in the area who initiate requests. </p>
<p>If you've got enough of those servers spread evenly through the geographic regions where you customers live, then you can significantly reduce the latency they experience.</p>
<p>One kind of edge computing that performs this function is known as a content distribution network (CDN). Cloudflare and Amazon's CloudFront are among the larger CDNs currently in operation.</p>
<p>Edge computing resources like those used by CDNs have also increasingly been used to manage large streams of data from and to IoT devices like the computers embedded in cars. Placing capable compute devices at the edges of large networks makes it possible to consume and transform such data sets faster than by moving the data all the way back to the more distant cloud.</p>
<h1 id="heading-what-are-the-key-compute-form-factors">What Are the Key Compute Form Factors?</h1>
<p>Computers, like egos, come in all shapes and sizes. Would you like to carry a rack full of bare metal servers around in your pocket to pay for your shopping? You're probably better off using some kind of mobile payment app on your smartphone.</p>
<p>Size matters. A lot. A device's form factor will determine the dimensions and capacity of its internal components. That means the particular motherboard, memory modules, storage drives, peripheral ports, and power supply you select for a device will be limited by your overall form factor.</p>
<p>The form factor you choose – for either a new project or just for your personal use – will generally be obvious (server racks can be heavy and don't handle travel well). But knowing what's available can make it easier to plan.</p>
<h2 id="heading-devices-using-video-displays">Devices using video displays</h2>
<p>The term <em>personal computer</em> (PC), these days, is used to describe either desktop or laptop computers. </p>
<p>Laptops, since they're designed to be mobile, are largely self-contained. Desktops, by contrast, generally come with the core compute elements within a box that includes external ports for connecting peripherals like keyboards and monitors. </p>
<p>While you can find computers with power and functionality that's comparable to PCs in very small (credit-card sized) cases, the larger boxes used by desktops allow for easier customization and upgrades.</p>
<p>Gaming consoles – like Sony's PlayStation, Microsoft's Xbox, and the Nintendo Switch – are effectively the equivalent of desktop PCs, except that their software is built on closed systems. </p>
<p>They're "closed" in the sense that their software interface exposes only the functionality the manufacturer wants you to see. Modifying or customizing the OS or internal works of a game console is normally impossible.</p>
<p>A touchscreen device uses the gestures and taps it senses from users as input devices in place of the traditional mouse or keyboard. Touchscreen technologies are the primary inspiration behind smaller form factors for consumers, since there's no need for external input devices. </p>
<p>This, more than just about anything else, has driven the tremendous growth of the tablet and smartphone markets. (It also explains the freakishly agile thumbs of entire generations of young people.)</p>
<h2 id="heading-devices-without-video-displays">Devices without video displays</h2>
<p>The router that connects devices to a network through either WiFi or ethernet cables contains pretty much the same internal motherboard and network interfaces that you'd find in any other compute device. </p>
<p>The big difference is that there's no HDMI, DVI, or VGA video port. Routers are meant to run autonomously and, when administration is necessary, it'll usually happen through a browser interface across a network.</p>
<p>You launch an admin session with your router by entering its IP address into your browser and authenticating when prompted. In some cases, you can also launch terminal sessions through the Secure Shell (SSH) protocol.</p>
<p>This remote administration model is shared by many display-less device types. Those will include medical (and non-medical) implants or <em>wearables</em> that come with tiny computers built to monitor, report, or even interact with their host environments. </p>
<p>(In the context of wearables, I would at least briefly discuss smart watches here but, for the life of me, I can't figure out why anyone would want one.)</p>
<p>Display-free computers are also embedded in medical devices, appliances, cars, logistics fleets and industrial machinery. All of those embedded computers are components of the growing internet of things.</p>
<p>Thanks for reading!</p>
<p><em>YouTube videos of all ten chapters from this book <a target="_blank" href="https://www.youtube.com/playlist?list=PLSiZCpRYoTZ6UWl4xialvwLOKyHYYJUiC">are available here</a>. Lots more tech goodness - in the form of books, courses, and articles - <a target="_blank" href="https://bootstrap-it.com">can be had here</a>. And consider taking my <a target="_blank" href="https://www.udemy.com/user/david-clinton-12/">AWS, security, and container technology courses here</a>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Provision a Home Lab with Oracle Cloud and Ansible ]]>
                </title>
                <description>
                    <![CDATA[ Imagine for a moment that you been working hard to setup a website, protected with SSL, and then your hardware fails. This means that unless you have a perfect backup of your machine, you will need to install all the software and configuration files ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/provision-home-lab-with-oracle-cloud-and-ansible/</link>
                <guid isPermaLink="false">66d85145e9c1a2c18adec0be</guid>
                
                    <category>
                        <![CDATA[ Cloud Computing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software architecture ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SSL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jose Vicente Nunez ]]>
                </dc:creator>
                <pubDate>Tue, 15 Nov 2022 21:52:50 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/11/pexels-pixabay-210158.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Imagine for a moment that you been working hard to setup a website, protected with SSL, and then your hardware fails. This means that unless you have a perfect backup of your machine, you will need to install all the software and configuration files by hand.</p>
<p>What if it's not just one server but many? The amount of time you will need to fix all of them will grow exponentially – and because is a manual process it will be more error-prone.</p>
<p>And then the nightmare scenario: You don't have an up-to-date backup, or you have incomplete backups. Or the worst – there are no backups at all. This last case is more common than you think, especially in home labs where you are tinkering and playing around with stuff by yourself.</p>
<p>In this tutorial, I'll show you how you can do a full infrastructure provisioning of a pair of web servers on a Cloud provider, with <a target="_blank" href="https://www.cloudflare.com/learning/ssl/what-is-ssl/">SSL</a> certificates and monitoring metrics with <a target="_blank" href="https://prometheus.io/docs/introduction/overview/">Prometheus</a>.</p>
<h2 id="heading-what-you-need-for-this-setup">What You Need for This Setup</h2>
<p>The first thing you need is a cloud provider. <a target="_blank" href="https://cloud.oracle.com/">Oracle Cloud</a> offers a <em>Free Tier version</em> of their cloud services, which allows you to setup virtual machines for free. This is great for a home lab with lots of rich features that you can use to try new tools and techniques.</p>
<p>You'll also need an automation tool. I used <a target="_blank" href="https://www.ansible.com/">Ansible</a> because its doesn't have many requirements (you only need an SSH daemon and public key authentication to get things going). I also like it because it works equally well regardless of the cloud environment you are trying to provision.</p>
<p>In this tutorial we will use the <a target="_blank" href="https://github.com/ansible/ansible">Open Source version</a> of this tool, as it is more than sufficient for our purposes.</p>
<h3 id="heading-whats-included-in-the-ansible-playbook">What's included in the Ansible playbook</h3>
<p>An <a target="_blank" href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.htmlhttps://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html">Ansible playbook</a> is nothing more than a set of instructions you define to execute <em>tasks</em> that will change the status of a host. These actions are carried out on an <a target="_blank" href="https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html">inventory of hosts</a> you define.</p>
<p>Here, you are going to learn about the following:</p>
<ul>
<li><p>How to <a target="_blank" href="https://www.redhat.com/sysadmin/ansible-dynamic-inventories">clean inventory sources</a> by using the proper layout in your playbooks.</p>
</li>
<li><p>How to provision two <a target="_blank" href="https://nginx.org/en/">NGINX</a> instances, with the request of their proper free SSL certificates using <a target="_blank" href="https://certbot.eff.org/instructions?ws=nginx&amp;os=pip">Certbot</a>.</p>
</li>
<li><p>How to set up the local Linux firewalls and add a Prometheus node_exporter agent and one scraper to collect that data.</p>
</li>
<li><p>Concepts like variables, roles (with task inclusion), and conditional execution.</p>
</li>
<li><p>Important techniques like task tagging, debug messages, and static validation with <a target="_blank" href="https://www.redhat.com/sysadmin/ansible-lint-YAML">ansible-lint</a>.</p>
</li>
</ul>
<p>All the code can be found in this <a target="_blank" href="https://github.com/josevnz/OracleCloudHomeLab">GitHub repository</a>.</p>
<h2 id="heading-what-you-should-know-before-trying-this">What You Should Know Before Trying This</h2>
<p>Because we will cover several tasks here, you will probably need to be familiar with several things (I'll provide links as we go along):</p>
<ul>
<li><p>This is not an <a target="_blank" href="https://www.redhat.com/sysadmin/ansible-introduction">introductory course on Ansible</a> but more of a "how all things fit together" with a more detailed, but not too complex, playbook.</p>
</li>
<li><p>An <a target="_blank" href="https://www.oracle.com/cloud/free/">OCI Cloud Free Tier</a> account</p>
</li>
<li><p>Privileged account, most likely <a target="_blank" href="https://www.sudo.ws/">SUDO</a></p>
</li>
<li><p>Basic knowledge of <a target="_blank" href="https://www.redhat.com/sysadmin/firewalld-rules-and-scenarios">TCP/IP and firewalls with firewalld</a></p>
</li>
<li><p>How to use <a target="_blank" href="https://www.redhat.com/sysadmin/how-manage-packages">RPM</a> and how to <a target="_blank" href="https://www.redhat.com/sysadmin/package-linux-applications-rpm">package</a> applications (we will not do that here, but it helps to understand when an RPM is better than a complex task in Ansible)</p>
</li>
</ul>
<h3 id="heading-what-is-not-included-here">What is not included here</h3>
<p>OCI Cloud has a <a target="_blank" href="https://docs.oracle.com/en-us/iaas/api/#/en/network-firewall/20211001/NetworkFirewallPolicy/UpdateNetworkFirewallPolicy">complete REST API</a> to manage a lot of aspects of their cloud environment. <a target="_blank" href="https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm#SDK_and_CLI_Configuration_File">Their setup page</a> (specifically the SDK) is also very detailed.</p>
<h2 id="heading-youll-probably-do-things-differently-in-production">You'll Probably Do Things Differently in Production.</h2>
<h3 id="heading-installing-the-oci-metrics-datasource-instead-of-prometheus-agents-on-a-virtual-machine">Installing the OCI-Metrics-datasource instead of Prometheus agents on a virtual machine</h3>
<p>You can go to <a target="_blank" href="https://grafana.com/grafana/plugins/oci-metrics-datasource/?tab=installation">this page</a> to install it on your Grafana instance (Bare metal or Cloud). Also you need to <a target="_blank" href="https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/grafana.htm">setup your credentials and permissions as explained here</a>.</p>
<p>This is probably the most efficient way to monitor your resources as you do not need to run agents on your virtual machines. But I will install instead a <a target="_blank" href="https://github.com/prometheus/node_exporter">Prometheus node_exporter agent</a> and <a target="_blank" href="https://github.com/prometheus/prometheus">scraper</a> that will be visible from a <a target="_blank" href="https://grafana.com/">Grafana Cloud</a> instance.</p>
<h3 id="heading-an-exposed-prometheus-on-the-internet-endpoint-is-not-a-good-idea">An exposed Prometheus on the Internet endpoint is not a good idea</h3>
<p>It is very clear, I'm exposing my Prometheus scraper to the Internet so Grafana cloud can reach it. On an Intranet with a private cloud and your local Grafana, this is not an issue – but here, a Prometheus agent pushing data to Grafana would be a better option.</p>
<p>Still, Grafana provides a <a target="_blank" href="https://grafana.com/docs/grafana-cloud/reference/allow-list/">list of public IP addresses</a> that you can use to setup your allow list.</p>
<p>So the following will work:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/oracle_cloud_ingress_rules.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Oracle Cloud Ingress Rules</em></p>
<p>But it is not the best. Instead, you want to restrict the specific IP addresses that can pull data from your exposed services. The prometheus exporter can be completely hidden from Grafana on port 9100. Instead we only need to expose the Prometheus scraper that listens on port 9000.</p>
<p>For this home lab, it is not a big deal having such services fully exposed. But if you have a server with sensitive data, you must restrict who can reach the service!</p>
<p>An alternative to the Prometheus endpoint is to push the data to Grafana <a target="_blank" href="https://grafana.com/docs/agent/latest/">by using a Grafana agent</a> but I will not cover that option here.</p>
<h1 id="heading-playbook-analysis">Playbook Analysis</h1>
<p>Ansible lets you have a single file with the playbook instructions, but eventually you will find that such a structure is difficult to maintain.</p>
<p>For my playbook I decided to keep the suggested structure:</p>
<pre><code class="lang-shell">tree -A 
.
├── inventory
│   └── cloud.yaml
├── oracle.yaml
├── roles
│   └── oracle
│       ├── files
│       │   ├── logrotate_prometheus-node-exporter
│       │   ├── prometheus-node-exporter
│       │   └── requirements_certboot.txt
│       ├── handlers
│       │   └── main.yaml
│       ├── meta
│       ├── tasks
│       │   ├── controller.yaml
│       │   ├── main.yaml
│       │   ├── metrics.yaml
│       │   └── nginx.yaml
│       ├── templates
│       │   ├── prometheus-node-exporter.service
│       │   ├── prometheus.service
│       │   └── prometheus.yaml
│       └── vars
│           └── main.yaml
└── site.yaml
</code></pre>
<p>Below is a brief description of how the content is organized:</p>
<ol>
<li><p>You can have more than one site. You control that inside the [site.yaml](file:///home/josevnz/OracleCloudHomeLab/site.yaml) file.</p>
</li>
<li><p>The host list is inside the inventory directory. You can have more than one inventory file or scripts to generate the hostlist, or a combination of both.</p>
</li>
<li><p>The roles/oracle group the tasks. We only have one role called 'oracle' because that's the cloud provider I'm focusing on here.</p>
</li>
<li><p>Our playbook uses metadata in the form of variables, with each one defined on the 'vars' directory. That way we can customize the behaviour of the playbook in multiple places:</p>
</li>
</ol>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-comment"># Common variables for my Oracle Cloud environments</span>
<span class="hljs-attr">controller_host:</span> <span class="hljs-string">XXXX.com</span>
<span class="hljs-attr">ssl_maintainer_email:</span> <span class="hljs-string">YYYYYY@ZZZZ.com</span>
<span class="hljs-attr">architecture:</span> <span class="hljs-string">arm64</span>
<span class="hljs-attr">prometheus_version:</span> <span class="hljs-number">2.38</span><span class="hljs-number">.0</span>
<span class="hljs-attr">prometheus_port:</span> <span class="hljs-number">9090</span>
<span class="hljs-attr">prometheus_node_exporter_nodes:</span> <span class="hljs-string">"['X-server1:<span class="hljs-template-variable">{{ node_exporter_port }}</span>', 'Y-server2:<span class="hljs-template-variable">{{ node_exporter_port }}</span>' ]"</span>
<span class="hljs-attr">node_exporter_version:</span> <span class="hljs-number">1.4</span><span class="hljs-number">.0</span>
<span class="hljs-attr">node_exporter_port:</span> <span class="hljs-number">9100</span>
<span class="hljs-attr">internal_network:</span> <span class="hljs-string">QQ.0.0.0/24</span>
</code></pre>
<p>The roles/oracle files directory contains files that can be copied as is to the remote directory. The templates' directory is similar, but the files in there can be customized for each host by using the <a target="_blank" href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_templating.html">Jinja templating language</a>.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># A template for the prometheus scraper configuration file</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">global:</span>
    <span class="hljs-attr">scrape_interval:</span> <span class="hljs-string">30s</span>
    <span class="hljs-attr">evaluation_interval:</span> <span class="hljs-string">30s</span>
    <span class="hljs-attr">scrape_timeout:</span> <span class="hljs-string">10s</span>
    <span class="hljs-attr">external_labels:</span>
        <span class="hljs-attr">monitor:</span> <span class="hljs-string">'oracle-cloud-metrics'</span>

<span class="hljs-attr">scrape_configs:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">job_name:</span> <span class="hljs-string">'node-exporter'</span>
    <span class="hljs-attr">static_configs:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">targets:</span> {{ <span class="hljs-string">prometheus_node_exporter_nodes</span> }}
    <span class="hljs-attr">tls_config:</span>
      <span class="hljs-attr">insecure_skip_verify:</span> <span class="hljs-literal">true</span>
</code></pre>
<p>The 'tasks' directory is where we store our tasks, that is the actions that will modify the server state. Note that Ansible will not execute tasks if it's not necessary. The idea is that you can re-run a playbook as many times as needed and the final state will be the same.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Fragment of the nginx tasks file. See how we notify a handler to restart nginx after the SSL certificate is renewed.</span>
<span class="hljs-meta">---</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Copy</span> <span class="hljs-string">requirements</span> <span class="hljs-string">file</span>
  <span class="hljs-attr">ansible.builtin.copy:</span>
    <span class="hljs-attr">src:</span> <span class="hljs-string">requirements_certboot.txt</span>
    <span class="hljs-attr">dest:</span> <span class="hljs-string">/opt/requirements_certboot.txt</span>
  <span class="hljs-attr">tags:</span> <span class="hljs-string">certbot_requirements</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Certbot</span>
  <span class="hljs-attr">pip:</span>
    <span class="hljs-attr">requirements:</span> <span class="hljs-string">/opt/requirements_certboot.txt</span>
    <span class="hljs-attr">virtualenv:</span> <span class="hljs-string">/opt/certbot/</span>
    <span class="hljs-attr">virtualenv_site_packages:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">virtualenv_command:</span> <span class="hljs-string">/usr/bin/python3</span> <span class="hljs-string">-m</span> <span class="hljs-string">venv</span>
  <span class="hljs-attr">tags:</span> <span class="hljs-string">certbot_env</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Get</span> <span class="hljs-string">SSL</span> <span class="hljs-string">certificate</span>
  <span class="hljs-attr">command:</span>
    <span class="hljs-attr">argv:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/opt/certbot/bin/certbot</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">--nginx</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">--agree-tos</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">-m</span> {{ <span class="hljs-string">ssl_maintainer_email</span> }}
      <span class="hljs-bullet">-</span> <span class="hljs-string">-d</span> {{ <span class="hljs-string">inventory_hostname</span> }}
      <span class="hljs-bullet">-</span> <span class="hljs-string">--non-interactive</span>
  <span class="hljs-attr">notify:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">Restart</span> <span class="hljs-string">Nginx</span>
  <span class="hljs-attr">tags:</span> <span class="hljs-string">certbot_install</span>
</code></pre>
<p>There is one special directory called 'handlers'. There we define actions that must happen if a task changes the state of our host.</p>
<p>We now have a picture of how all the pieces work together, so let's talk about some specific details.</p>
<h3 id="heading-firewall-provisioning">Firewall provisioning</h3>
<p>With Ansible, you can replace a sequence of commands like this:</p>
<pre><code class="lang-python">sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload
</code></pre>
<p>With a <a target="_blank" href="https://docs.ansible.com/ansible/latest/collections/ansible/posix/firewalld_module.html">firewalld module</a>:</p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Enable</span> <span class="hljs-string">HTTP</span> <span class="hljs-string">at</span> <span class="hljs-string">the</span> <span class="hljs-string">Linux</span> <span class="hljs-string">firewall</span>
  <span class="hljs-attr">firewalld:</span>
    <span class="hljs-attr">zone:</span> <span class="hljs-string">public</span>
    <span class="hljs-attr">service:</span> <span class="hljs-string">http</span>
    <span class="hljs-attr">permanent:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">state:</span> <span class="hljs-string">enabled</span>
    <span class="hljs-attr">immediate:</span> <span class="hljs-literal">yes</span>
  <span class="hljs-attr">notify:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">Reload</span> <span class="hljs-string">firewall</span>
  <span class="hljs-attr">tags:</span> <span class="hljs-string">firewalld_https</span>

<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Enable</span> <span class="hljs-string">HTTPS</span> <span class="hljs-string">at</span> <span class="hljs-string">the</span> <span class="hljs-string">Linux</span> <span class="hljs-string">firewall</span>
  <span class="hljs-attr">firewalld:</span>
    <span class="hljs-attr">zone:</span> <span class="hljs-string">public</span>
    <span class="hljs-attr">service:</span> <span class="hljs-string">https</span>
    <span class="hljs-attr">permanent:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">state:</span> <span class="hljs-string">enabled</span>
    <span class="hljs-attr">immediate:</span> <span class="hljs-literal">yes</span>
  <span class="hljs-attr">notify:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">Reload</span> <span class="hljs-string">firewall</span>
  <span class="hljs-attr">tags:</span> <span class="hljs-string">firewalld_https</span>
</code></pre>
<h3 id="heading-common-tasks-have-nice-replacements"><strong>Common tasks have nice replacements</strong></h3>
<p>So instead of running SUDO with a privileged command:</p>
<pre><code class="lang-python">sudo dnf install -y nginx
sudo systemctl enable nginx.service --now
</code></pre>
<p>You can have something like this:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># oracle.yaml file, which tells which roles to call, included from site.yaml</span>
<span class="hljs-meta">---</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">hosts:</span> <span class="hljs-string">oracle</span>
  <span class="hljs-attr">serial:</span> <span class="hljs-number">2</span>
  <span class="hljs-attr">remote_user:</span> <span class="hljs-string">opc</span>
  <span class="hljs-attr">become:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">become_user:</span> <span class="hljs-string">root</span>
  <span class="hljs-attr">roles:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">oracle</span>
<span class="hljs-comment"># NGINX task (roles/oracle/tasks/nginx.yaml)</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Ensure</span> <span class="hljs-string">nginx</span> <span class="hljs-string">is</span> <span class="hljs-string">at</span> <span class="hljs-string">the</span> <span class="hljs-string">latest</span> <span class="hljs-string">version</span>
  <span class="hljs-attr">dnf:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span> <span class="hljs-string">&gt;=</span> <span class="hljs-number">1.14</span><span class="hljs-number">.1</span>
    <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
    <span class="hljs-attr">update_cache:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">tags:</span> <span class="hljs-string">install_nginx</span>
<span class="hljs-comment"># And a handler that will restart NGINX after it gets modified (handlers/main.yaml)</span>
<span class="hljs-meta">---</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Restart</span> <span class="hljs-string">Nginx</span>
  <span class="hljs-attr">ansible.builtin.service:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">state:</span> <span class="hljs-string">restarted</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Reload</span> <span class="hljs-string">firewall</span>
  <span class="hljs-attr">ansible.builtin.systemd:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">firewalld.service</span>
    <span class="hljs-attr">state:</span> <span class="hljs-string">reloaded</span>
</code></pre>
<h2 id="heading-how-to-run-the-playbooks">How to Run the Playbooks</h2>
<p>Normally you don't wait to have the whole playbook written, but you run the pieces you need in the proper order. At some point you will have your whole playbook finished and ready to go.</p>
<h3 id="heading-make-sure-the-playbook-behaves-properly-with-check-before-making-any-changes">Make sure the playbook behaves properly with <code>--check</code> before making any changes</h3>
<p>The very first step is to check your playbook file for errors. For that you can use yamllint:</p>
<pre><code class="lang-shell">yamllint roles/oracle/tasks/main.yaml
</code></pre>
<p>But doing this for every yaml file in your playbook can be tedious an error-prone. As an alternative, you can run the playbook in a 'dry-run' mode, to see what will happen without actually making any changes:</p>
<p><a target="_blank" href="https://asciinema.org/a/537302"><img src="https://asciinema.org/a/537302.svg" alt="asciicast" width="600" height="400" loading="lazy"></a></p>
<p>Another way to gradually test a complex playbook is by executing a specific task by using a tag or group of tags. That way you can do controlled execution of your playbook:</p>
<p><em>Keep in mind that this will not execute any dependencies that you may have defined on you playbook, tough</em>:</p>
<p><a target="_blank" href="https://asciinema.org/a/537303"><img src="https://asciinema.org/a/537303.svg" alt="asciicast" width="600" height="400" loading="lazy"></a></p>
<h3 id="heading-use-ansible-lint-when-ansible-playbook-check-is-not-good-enough"><strong>Use Ansible-lint when ansible-playbook --check is not good enough</strong></h3>
<p>Some errors are more subtle and will not get caught with <code>ansible-playbook --check</code>. To get a more complete check on your playbooks before minor issues become a headache you can use <a target="_blank" href="https://ansible-lint.readthedocs.io/philosophy/">ansible-lint</a>. So let's get it installed:</p>
<pre><code class="lang-shell">python3 -m venv ~/virtualenv/ansiblelint &amp;&amp; . ~/virtualenv/ansiblelint/bin/activate
pip install --upgrade pip
pip install --upgrade wheel
pip install ansible-lint
</code></pre>
<p>Now we can check the playbook:</p>
<pre><code class="lang-shell">(ansiblelint) [josevnz@dmaf5 OracleCloudHomeLab]$ ansible-lint site.yaml 
WARNING  Overriding detected file kind 'yaml' with 'playbook' for given positional argument: site.yaml
WARNING  Listing 1 violation(s) that are fatal
syntax-check[specific]: couldn't resolve module/action 'firewalld'. This often indicates a misspelling, missing collection, or incorrect module path.
roles/oracle/tasks/nginx.yaml:2:3
</code></pre>
<p>Strange, firewalld is available on our Ansible installation. What else was installed by ansible-lint?</p>
<pre><code class="lang-shell">(ansiblelint) [josevnz@dmaf5 OracleCloudHomeLab]$ ansible --version
ansible [core 2.14.0]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/josevnz/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/josevnz/virtualenv/ansiblelint/lib64/python3.9/site-packages/ansible
  ansible collection location = /home/josevnz/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/josevnz/virtualenv/ansiblelint/bin/ansible
  python version = 3.9.9 (main, Nov 19 2021, 00:00:00) [GCC 10.3.1 20210422 (Red Hat 10.3.1-1)] (/home/josevnz/virtualenv/ansiblelint/bin/python3)
  jinja version = 3.1.2
  libyaml = True
</code></pre>
<p>Ansible-lint installed its own ansible [core], and firewalld is part of <a target="_blank" href="https://docs.ansible.com/ansible/latest/collections/ansible/posix/firewalld_module.html">ansible.posix collection</a>. We will use <a target="_blank" href="https://docs.ansible.com/ansible/latest/cli/ansible-galaxy.html">Ansible Galaxy</a> to install it:</p>
<pre><code class="lang-shell">(ansiblelint) [josevnz@dmaf5 OracleCloudHomeLab]$ which ansible-galaxy
~/virtualenv/ansiblelint/bin/ansible-galaxy
(ansiblelint) [josevnz@dmaf5 OracleCloudHomeLab]$ ansible-galaxy collection install ansible.posix
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/download/ansible-posix-1.4.0.tar.gz to /home/josevnz/.ansible/tmp/ansible-local-18099xpw_8usc/tmp8msc9uf5/ansible-posix-1.4.0-_f17f525
Installing 'ansible.posix:1.4.0' to '/home/josevnz/.ansible/collections/ansible_collections/ansible/posix'
ansible.posix:1.4.0 was installed successfully
</code></pre>
<p>Running it again:</p>
<pre><code class="lang-shell">(ansiblelint) [josevnz@dmaf5 OracleCloudHomeLab]$ ansible-lint site.yaml 
WARNING  Overriding detected file kind 'yaml' with 'playbook' for given positional argument: site.yaml
WARNING  Listing 50 violation(s) that are fatal
name[play]: All plays should be named. (warning)
oracle.yaml:2

fqcn[action-core]: Use FQCN for builtin module actions (service).
roles/oracle/handlers/main.yaml:2 Use `ansible.builtin.service` or `ansible.legacy.service` instead.

fqcn[action-core]: Use FQCN for builtin module actions (command).
roles/oracle/handlers/main.yaml:6 Use `ansible.builtin.command` or `ansible.legacy.command` instead.
</code></pre>
<p>Some warnings are pedantic ('Use FQCN for builtin module actions (command)') and others require attention (Commands should not change things if nothing needs doing.).</p>
<p>Ansible-lint found many smells on the playbook, there is one option to re-write the files and correct some of these errors automatically:</p>
<p><a target="_blank" href="https://asciinema.org/a/538053"><img src="https://asciinema.org/a/538053.svg" alt="asciicast" width="600" height="400" loading="lazy"></a></p>
<p>There are some guidelines you can <a target="_blank" href="https://ansible-lint.readthedocs.io/profiles/#production">follow to correct these issues</a>. Below are some that can be directly applied to the warnings we got earlier:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/ansible_error_fixes.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Note that all the errors are easy to solve. Some commands decide on their own if they should make changes or not but have a hard time communicating back to Ansible:</p>
<pre><code class="lang-yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Get</span> <span class="hljs-string">SSL</span> <span class="hljs-string">certificate</span>
  <span class="hljs-attr">ansible.builtin.shell:</span>
    <span class="hljs-attr">argv:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/opt/certbot/bin/certbot</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">--nginx</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">--agree-tos</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">-m</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ ssl_maintainer_email }}</span>"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">-d</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ inventory_hostname }}</span>"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">--non-interactive</span>
  <span class="hljs-attr">notify:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">Restart</span> <span class="hljs-string">Nginx</span>
  <span class="hljs-attr">tags:</span> <span class="hljs-string">certbot_install</span>
</code></pre>
<p>In our case, certboot prints a message if the certificate is not yet due for renewal. If that output is missing then we trigger the Nginx restart (see <a target="_blank" href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_error_handling.html#defining-changed">defining changed</a>):</p>
<pre><code class="lang-yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Get</span> <span class="hljs-string">SSL</span> <span class="hljs-string">certificate</span>
  <span class="hljs-attr">ansible.builtin.shell:</span>
    <span class="hljs-attr">argv:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/opt/certbot/bin/certbot</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">--nginx</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">--agree-tos</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">-m</span> {{ <span class="hljs-string">ssl_maintainer_email</span> }}
      <span class="hljs-bullet">-</span> <span class="hljs-string">-d</span> {{ <span class="hljs-string">inventory_hostname</span> }}
      <span class="hljs-bullet">-</span> <span class="hljs-string">--non-interactive</span>
  <span class="hljs-attr">register:</span> <span class="hljs-string">certbot_output</span> <span class="hljs-comment"># Registers the certbot output.</span>
  <span class="hljs-attr">changed_when:</span> 
    <span class="hljs-bullet">-</span> <span class="hljs-string">'"Certificate not yet due for renewal" not in certbot_output.stdout'</span>
  <span class="hljs-attr">notify:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">Restart</span> <span class="hljs-string">Nginx</span>
  <span class="hljs-attr">tags:</span> <span class="hljs-string">certbot_install</span>
</code></pre>
<p>I do want to use shell, as I need to expand the variable for certbot, but ansible-lint is still not happy:</p>
<pre><code class="lang-shell">(ansiblelint) [josevnz@dmaf5 OracleCloudHomeLab]$ ansible-lint site.yaml
WARNING  Overriding detected file kind 'yaml' with 'playbook' for given positional argument: site.yaml
WARNING  Listing 1 violation(s) that are fatal
command-instead-of-shell: Use shell only when shell functionality is required.
roles/oracle/tasks/nginx.yaml:47 Task/Handler: Get SSL certificate

You can skip specific rules or tags by adding them to your configuration file:
# .config/ansible-lint.yml
warn_list:  # or 'skip_list' to silence them completely
  - command-instead-of-shell  # Use shell only when shell functionality is required.

                   Rule Violation Summary                    
 count tag                      profile rule associated tags 
     1 command-instead-of-shell basic   command-shell, idiom 

Failed after min profile: 1 failure(s), 0 warning(s) on 8 files.
</code></pre>
<p>Time to treat this error as a warning, as I know they are not issues, by creating a <code>.config/ansible-lint.yml</code>:</p>
<pre><code class="lang-shell">(ansiblelint) [josevnz@dmaf5 OracleCloudHomeLab]$ ansible-lint site.yaml
WARNING  Overriding detected file kind 'yaml' with 'playbook' for given positional argument: site.yaml
WARNING  Listing 1 violation(s) that are fatal
command-instead-of-shell: Use shell only when shell functionality is required. (warning)
roles/oracle/tasks/nginx.yaml:47 Task/Handler: Get SSL certificate


                        Rule Violation Summary                         
 count tag                      profile rule associated tags           
     1 command-instead-of-shell basic   command-shell, idiom (warning) 

Passed with min profile: 0 failure(s), 1 warning(s) on 8 files.
</code></pre>
<p>Much better now, the warning is not treated as an error.</p>
<h3 id="heading-jinja-best-practices"><strong>Jinja best practices</strong></h3>
<p>If you plan to use variables and Jinja templates, make sure you quote them (example: "dest: /opt/prometheus-{{ prometheus_version }}.linux-{{ architecture }}.tar.gz")</p>
<h3 id="heading-constrain-where-the-playbook-runs-with-limit-and-tags">Constrain where the playbook runs with <code>--limit</code> and <code>--tags</code></h3>
<p>Say that you are only interested in running your playbook on a certain host. In that case, you can also do that by using the <code>--limit</code> flag:</p>
<pre><code class="lang-shell">ansible-playbook --inventory inventory --limit fido.yourcompany.com --tags certbot_renew site.yaml
</code></pre>
<p><a target="_blank" href="https://asciinema.org/a/537304"><img src="https://asciinema.org/a/537304.svg" alt="asciicast" width="600" height="400" loading="lazy"></a></p>
<p>Here we did run only a task tagged certbot_renew on the host fido.yourcompany.com.</p>
<h3 id="heading-how-to-deal-with-a-real-issue">How to deal with a real issue</h3>
<p>Let's make this interesting: say that I was eager to update one of my requirements for certboot, and I changed versions if pip to '22.3.1':</p>
<pre><code class="lang-text">pip==22.3.1
wheel==0.38.4
certbot==1.32.0
certbot-nginx==1.32.0
</code></pre>
<p>When I run the playbook we have a failure:</p>
<p><a target="_blank" href="https://asciinema.org/a/537318"><img src="https://asciinema.org/a/537318.svg" alt="asciicast" width="600" height="400" loading="lazy"></a></p>
<p>This is an issue with the versions if specified on the <a target="_blank" href="https://github.com/josevnz/OracleCloudHomeLab/blob/main/roles/oracle/files/requirements_certboot.txt">requirements_certboot.txt</a> file. When you install a Python library using a virtual environment you can specify versions like this:</p>
<p>pip==22.3.1 wheel==0.38.1 certbot==1.23.0 certbot-nginx==1.23.0</p>
<p>To fix the issue, we will revert the versions used on the file and then re-run the requirements file and Certbot installation task:</p>
<pre><code class="lang-yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Certbot</span>
  <span class="hljs-attr">pip:</span>
    <span class="hljs-attr">requirements:</span> <span class="hljs-string">/opt/requirements_certboot.txt</span>
    <span class="hljs-attr">virtualenv:</span> <span class="hljs-string">/opt/certbot/</span>
    <span class="hljs-attr">virtualenv_site_packages:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">virtualenv_command:</span> <span class="hljs-string">/usr/bin/python3</span> <span class="hljs-string">-m</span> <span class="hljs-string">venv</span>
    <span class="hljs-attr">state:</span> <span class="hljs-string">forcereinstall</span>
  <span class="hljs-attr">tags:</span> <span class="hljs-string">certbot_env</span>
</code></pre>
<pre><code class="lang-shell">ansible-playbook --inventory inventory --tags certbot_env site.yaml
</code></pre>
<p>See it in action:</p>
<p><a target="_blank" href="https://asciinema.org/a/537320"><img src="https://asciinema.org/a/537320.svg" alt="asciicast" width="600" height="400" loading="lazy"></a></p>
<h3 id="heading-how-to-run-the-whole-playbook">How to run the whole playbook</h3>
<pre><code class="lang-shell">ansible-playbook --inventory inventory site.yaml
</code></pre>
<p>It is time to run the whole playbook:</p>
<p><a target="_blank" href="https://asciinema.org/a/537322"><img src="https://asciinema.org/a/537322.svg" alt="asciicast" width="600" height="400" loading="lazy"></a></p>
<h2 id="heading-wrapping-up">Wrapping up</h2>
<p>This tutorial only touches the surface of what you can do with Ansible. So below are a few more resources you should explore to learn more:</p>
<ul>
<li><p>Improving inventories: <a target="_blank" href="https://www.redhat.com/sysadmin/ansible-dynamic-inventories">How to create dynamic inventory files in Ansible</a>, <a target="_blank" href="https://www.redhat.com/sysadmin/ansible-dynamic-inventory-python">How to write a Python script to create dynamic Ansible inventories</a>, <a target="_blank" href="https://www.redhat.com/sysadmin/ansible-plugin-inventory-files">How to write an Ansible plugin to create inventory files</a></p>
</li>
<li><p>Sometimes your playbooks will run slow, and you may need to <a target="_blank" href="https://www.redhat.com/sysadmin/ansible-callback-plugins-metrics">Assess resource consumption with Ansible callback plugins</a>.</p>
</li>
<li><p>And there will be a time <a target="_blank" href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_debugger.html">when deeper debugging</a> is needed.</p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Download Applications Fast Using Mirrors On Manjaro Linux ]]>
                </title>
                <description>
                    <![CDATA[ If you are running Linux OS, you've likely already heard about mirror repositories. According to Quora, In Linux, a mirror is a copy of programs available for download. If you are close (in networking terms, maybe or maybe not geographically) to one... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-download-application-fast-on-manjaro-linux/</link>
                <guid isPermaLink="false">66b902c8626438a622a1ef7b</guid>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Md. Fahim Bin Amin ]]>
                </dc:creator>
                <pubDate>Wed, 09 Nov 2022 18:57:33 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/11/1631592932_Manjaro-Linux-211-with-fresh-desktop-environments.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you are running Linux OS, you've likely already heard about mirror repositories. According to Quora,</p>
<blockquote>
<p>In Linux, a mirror is a copy of programs available for download. If you are close (in networking terms, maybe or maybe not geographically) to one of the mirror sites listed, you might choose the mirror site as your main source of downloads so you get better response times.</p>
</blockquote>
<p>Different Linux-based operating systems have different methods to help you choose the fastest mirrors. But most of the workarounds are pretty much the same.</p>
<p>I've used <a target="_blank" href="https://manjaro.org/">Manjaro</a> for a pretty long time, and it's one of the most popular Linux-based operating systems out there. So I decided to write this article about it.</p>
<p>As many of us like to use the CLI (Command Line Interface) for downloading the necessary applications and packages in Linux-based operating systems, having a decent internet speed is very useful. Mirror repositories/servers help us with that.</p>
<h2 id="heading-why-mirror-servers-are-useful">Why Mirror Servers Are Useful</h2>
<p>As Linux is getting more and more popular, there are a lot of servers getting created in various countries that keep the same data that is present in the official servers. </p>
<p>We call these mirror servers because those servers only mirror (copy) the original data from the original sources and keep those data in their servers. This helps users nearer to them get the data at a decent speed.</p>
<p>Also, these mirror servers/repositories lessen the pressure on the global international servers. </p>
<p>But keep in mind, all mirror servers might not have a decent reputation for containing malware/non-updated data and so on. The best practice before adding any mirror server is to search on Google/Reddit for that. </p>
<p>I always prefer the official community on Reddit as I can get legit information from their large user base. </p>
<p>If you simply search on Reddit, then you'll get countless subreddits for Linux users. Official forums and docs are also really helpful for this information. </p>
<p>By default, Linux Operating Systems comes with global server/repositories for downloading applications and packages, as they have customers from all over the globe. But if you want to switch to a specific server from where you can download the necessary packages at a decent speed, you can do that manually.</p>
<h3 id="heading-mirror-server-use-case">Mirror Server Use Case</h3>
<p>If you are not super familiar with these mirror servers/repositories, let me also provide you with a real-case scenario.</p>
<p>Suppose, you want to download a file, and that file is hosted on multiple servers that are situated in different countries. </p>
<p>Let's say you are in Bangladesh, and you want to download an application. When you start downloading the application file, it starts downloading from the global international server located in the USA. Naturally, it will take longer to download from that server given the long distance, right?</p>
<p>But it might also be possible that the same application file is also hosted in India, a country near your country. If you download the application file from the Indian server instead, then it will definitely take less time. </p>
<p>That's because this server is nearer than the other one in the USA – so the distance the data has to travel is smaller. Thus, you can download and get your application file faster.</p>
<p>Now let's see how to enable a mirror server.</p>
<h2 id="heading-how-to-enable-the-fastest-mirror-on-manjaro-linux">How to Enable the Fastest Mirror on Manjaro Linux</h2>
<p>Start by simply opening your terminal. Then apply the following command:</p>
<pre><code class="lang-bash">sudo pacman-mirrors --country all --api --protocols all --set-branch stable &amp;&amp; sudo pacman -Syyu
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/Screenshot_20220318_034726.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Enter the password and press the <code>Enter</code> key.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/Screenshot_20220318_034736.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>It'll take some time depending on your internet speed. Then it will automatically select the fastest mirror it can find for you. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/Screenshot_20220318_034748.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>After that, I would suggest that you reboot your PC / logout and log in to the session again. </p>
<p>That's it! If you want to know more about it, then the <a target="_blank" href="https://wiki.manjaro.org/index.php/Pacman-mirrors">official wiki</a> is also available for you here.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Thanks for reading the entire article. If it helps you then you can also check out other articles of mine at <a target="_blank" href="https://www.freecodecamp.org/news/author/fahimbinamin/">freeCodeCamp</a>.</p>
<p>If you want to get in touch with me, then you can do so using <a target="_blank" href="https://twitter.com/Fahim_FBA">Twitter</a>, <a target="_blank" href="https://www.linkedin.com/in/fahimfba/">LinkedIn</a>, and <a target="_blank" href="https://github.com/FahimFBA">GitHub</a>. </p>
<p>You can also <a target="_blank" href="https://www.youtube.com/@FahimAmin?sub_confirmation=1">SUBSCRIBE to my YouTube channel</a> (Code With FahimFBA) if you want to learn various kinds of programming languages with a lot of practical examples regularly.</p>
<p>If you want to check out my highlights, then you can do so at my <a target="_blank" href="https://www.polywork.com/fahimbinamin">Polywork timeline</a>.</p>
<p>You can also <a target="_blank" href="https://fahimbinamin.com/">visit my website</a> to learn more about me and what I'm working on.</p>
<p>Thanks a bunch!</p>
<p><em>The cover image has been taken from <a target="_blank" href="https://marketresearchtelecast.com/manjaro-linux-21-1-with-fresh-desktop-environments/155191/">here</a>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ DNS Server Not Responding – Service Unavailable DNS Failure [Solved] ]]>
                </title>
                <description>
                    <![CDATA[ Sometimes, you might suddenly discover that you can’t access the internet on your computer because of the error “DNS server not responding”.  If you run a troubleshooter for the issue, you'll get a message like the below: In your Chrome browser, you... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/dns-server-not-responding-service-unavailable-dns-failure-solved/</link>
                <guid isPermaLink="false">66adf0bbf452caf50fb1fde8</guid>
                
                    <category>
                        <![CDATA[ dns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ error ]]>
                    </category>
                
                    <category>
                        <![CDATA[ internet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kolade Chris ]]>
                </dc:creator>
                <pubDate>Mon, 11 Apr 2022 15:59:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/04/binary-g3068c576e_1920.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Sometimes, you might suddenly discover that you can’t access the internet on your computer because of the error “DNS server not responding”. </p>
<p>If you run a troubleshooter for the issue, you'll get a message like the below:
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss1-1.png" alt="ss1-1" width="600" height="400" loading="lazy"></p>
<p>In your Chrome browser, you might also get an error like the one below:
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss2-2.png" alt="ss2-2" width="600" height="400" loading="lazy"></p>
<p>This is because the Domain Name System (DNS) server is crucial in getting an internet connection on your computer.</p>
<p>As far as websites are concerned, the “DNS server not responding” error could be caused by DNS gaps and a DDoS (Distributed Denial of Service) attack. If this is the problem, you might have to wait for 72 hours for domain gaps to be fixed or the website admins to fix the security issues with the website.</p>
<p>On the user’s end, the “DNS server not responding” error could be caused by numerous reasons such as misconfigured DNS settings and outdated browsers. </p>
<p>If this is the cause, I will show you 7 ways to fix the error so you can restore your internet connection.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-how-does-the-dns-system-work">How Does the DNS System Work?</a></li>
<li><a class="post-section-overview" href="#heading-7-ways-to-fix-the-dns-server-not-responding-error">7 Ways to Fix the DNS Server Not Responding Error</a><ul>
<li><a class="post-section-overview" href="#heading-solution-1-switch-browsers">Solution 1: Switch Browsers</a></li>
<li><a class="post-section-overview" href="#heading-solution-2-temporarily-disable-your-antivirus">Solution 2: Temporarily Disable Your Antivirus</a></li>
<li><a class="post-section-overview" href="#heading-solution-3-restart-your-router-or-modem">Solution 3: Restart your Router or Modem</a></li>
<li><a class="post-section-overview" href="#heading-solution-4-flush-your-dns-cache">Solution 4: Flush your DNS Cache</a></li>
<li><a class="post-section-overview" href="#heading-solution-5-manually-change-your-dns-server">Solution 5: Manually Change your DNS Server</a></li>
<li><a class="post-section-overview" href="#heading-solution-6-update-your-network-adapter-driver">Solution 6: Update Your Network Adapter Driver</a></li>
<li><a class="post-section-overview" href="#heading-solution-7-disable-ipv6">Solution 7: Disable IPv6</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a><h2 id="heading-how-does-the-dns-system-work">How Does the DNS System Work?</h2>
</li>
</ul>
<p>Whenever you try to access a website, like freeCodeCamp.org, you type in the URL like “freecodecamp.org” to the address bar and hit <code>ENTER</code>. </p>
<p>Under the hood, the DNS server looks up the numerical address for freeCodeCamp.org. This numerical address is called an Internet Protocol (IP) address.</p>
<p>Once the browser gets this IP address, the website (freeCodeCamp.org or any other) will be shown to you. If the browser fails to find this address, then you might get the “DNS server not responding” error.</p>
<h2 id="heading-7-ways-to-fix-the-dns-server-not-responding-error">7 Ways to Fix the DNS Server Not Responding Error</h2>
<p>Now let's go through seven ways you can use to get rid of the “DNS server not responding” error so your internet connection can be restored.</p>
<h3 id="heading-solution-1-switch-browsers">Solution 1: Switch Browsers</h3>
<p>The “DNS server not responding” error could be showing up because of the browser you’re currently using. Some browsers have their own DNS cache and if there’s an issue with the cache, your internet experience on that browser could be negatively affected. </p>
<p>So, a non-complicated fix is to change to a different browser and see if the error persists. </p>
<p>For example, if you are using Chrome, switch to Edge if you are on Windows or Safari if you are using Mac. </p>
<p>If the website loads up in another browser, then you might need to update your other browser or reinstall it.</p>
<h3 id="heading-solution-2-temporarily-disable-your-antivirus">Solution 2: Temporarily Disable Your Antivirus</h3>
<p>Antivirus programs are notorious for interfering with applications and stopping them from working properly.</p>
<p>If you are getting the “DNS server not responding” error, consider disabling your antivirus program to see if your internet connection works fine. </p>
<p>If you are able to access the internet after disabling the antivirus, then it is the reason you’re getting the error. </p>
<p>In this case, you may want to consider getting another antivirus program.</p>
<p>If you are on Windows 10, you can disable Windows Security (AKA Windows Defender) by following the steps below:
<strong>Step 1</strong>: Press <code>ALT</code> + <code>SHIFT</code> + <code>ESC</code> on your keyboard to open the Task Manager</p>
<p><strong>Step 2</strong>: Switch to the Startup tab</p>
<p><strong>Step 3</strong>: Locate your Antivirus Program in the list, right-click on it and select "Disable".
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss3-1.png" alt="ss3-1" width="600" height="400" loading="lazy"></p>
<h3 id="heading-solution-3-restart-your-router-or-modem">Solution 3: Restart your Router or Modem</h3>
<p>If your internet connection relies on a router or modem, restarting it could help you get rid of the “DNS server not responding” error. </p>
<p>This is because turning off and then turning on a router or modem clears the cache of IP addresses. This could fix the error in the long run.</p>
<p>To restart your router or modem, locate the power button and long press to turn it off, then turn it on again.</p>
<h3 id="heading-solution-4-flush-your-dns-cache">Solution 4: Flush your DNS Cache</h3>
<p>If the “DNS server not responding” error is due to misconfiguration on your device, flushing your DNS is one of the most reliable ways to fix it. This is because the process would remove invalid IP configurations and outdated information in the DNS cache.</p>
<p>To flush your computer’s DNS on Windows, follow the steps highlighted below:</p>
<p><strong>Step 1</strong>: Hit the <code>WIN</code> button on your keyboard and search for "cmd". Then select "Run as Administrator" on the right.</p>
<p><strong>Step 2</strong>: Enter and execute the following commands one after the other:</p>
<ul>
<li><code>ipconfig /flushdns</code></li>
<li><code>ipconfig /release</code></li>
<li><code>ipconfig /renew</code>
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss4.png" alt="ss4" width="600" height="400" loading="lazy"></li>
</ul>
<p><strong>Step 3</strong>: Restart your computer</p>
<h3 id="heading-solution-5-manually-change-your-dns-server">Solution 5: Manually Change your DNS Server</h3>
<p>Using the default DNS server of your internet service provider could be the reason you are getting the “DNS server not responding” error.</p>
<p>You can change your DNS server to one of the free ones provided by the likes of Google and Cloudflare.</p>
<p>The steps below show you how to change your DNS server to Google's: </p>
<p><strong>Step 1</strong>: Right-click on Start and select “Network Connections”:
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss5.png" alt="ss5" width="600" height="400" loading="lazy"></p>
<p><strong>Step 2</strong>: Scroll down and select “Change adapter options”:
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss6.png" alt="ss6" width="600" height="400" loading="lazy"></p>
<p><strong>Step 3</strong>: In the pop-up that appears, right-click on the network you are connected to and select “Properties”:
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss7.png" alt="ss7" width="600" height="400" loading="lazy"></p>
<p><strong>Step 4</strong>: In the next pop-up that appears, double-click on “Internet Protocol Version 4 (TCP/IPv4)”:
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss8.png" alt="ss8" width="600" height="400" loading="lazy"></p>
<p><strong>Step 5</strong>: In the following pop-up that appears, click the radio button that says “Use the following DNS server addresses”:
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss9.png" alt="ss9" width="600" height="400" loading="lazy"></p>
<p><strong>Step 6</strong>: Enter 8.8.8.8 for “Preferred DNS server” and 8.8.4.4 for “Alternate DNS server”:
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss10.png" alt="ss10" width="600" height="400" loading="lazy"></p>
<p>This is the free DNS server provided by Google.</p>
<p><strong>Step 7</strong>: Click “Ok”, and “Ok” again.</p>
<p>N.B.: If your computer is configured to use IPv6 instead of IPv4, then in step 4, you should choose “Internet Protocol Version 6 (TCP/IPv6)” instead of “Internet Protocol Version 4 (TCP/IPv4)”.</p>
<h3 id="heading-solution-6-update-your-network-adapter-driver">Solution 6: Update Your Network Adapter Driver</h3>
<p>Updating your network adapter driver can fix a lot of technical issues – including the “DNS server not responding” error, since the new driver could include bug fixes.</p>
<p>To update your network adapter driver, you can do it with the steps below:</p>
<p><strong>Step 1</strong>: Click on Start and select Device Manager.</p>
<p><strong>Step 2</strong>: Expand Network Adapters.</p>
<p><strong>Step 3</strong>: Right-click on the affected driver and select Update driver:
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss11.png" alt="ss11" width="600" height="400" loading="lazy"></p>
<p><strong>Step 4</strong>: Choose Search automatically for updated driver software:
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss12.png" alt="ss12" width="600" height="400" loading="lazy"></p>
<p><strong>Step 5</strong>: Allow your computer to search for a driver online and install it for you. When it is done installing, restart your computer.</p>
<h3 id="heading-solution-7-disable-ipv6">Solution 7: Disable IPv6</h3>
<p>If your current network is configured to use IPv4 and IPv6 is turned on on your computer, it could lead to negative interference which could make you get the “DNS server not responding” error.</p>
<p>To disable IPv6, the following steps can help you:</p>
<p><strong>Step 1</strong>: Right-click on Start and select “Network Connections”:
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss5.png" alt="ss5" width="600" height="400" loading="lazy"></p>
<p><strong>Step 2</strong>: Scroll down and select “Change adapter options”:
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss6.png" alt="ss6" width="600" height="400" loading="lazy"></p>
<p><strong>Step 3</strong>: In the pop-up that appears, right-click on the network you are connected to and select “Properties”:
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss7.png" alt="ss7" width="600" height="400" loading="lazy"></p>
<p><strong>Step 4</strong>: In the next pop-up that appears, uncheck “Internet Protocol Version 6 (TCP/IPv6)”:
<img src="https://www.freecodecamp.org/news/content/images/2022/04/ss13.png" alt="ss13" width="600" height="400" loading="lazy"></p>
<p><strong>Step 6</strong>: Click “Ok”, and “Ok” again.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>The “DNS server not responding” error can be frustrating and disturb your internet experience. But in this article you've learned how to fix it if the error is caused by misconfiguration of DNS from the user’s end. </p>
<p>I hope one of the solutions to the error explained in this article helps you fix the error.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Secure Server with Node.js and Express and Upload Images with Cloudinary ]]>
                </title>
                <description>
                    <![CDATA[ By Njoku Samson Ebere In this tutorial, we will learn how to create a server. We will begin without express and then strengthen the server using express. After that, we will see how to upload images to Cloudinary from the app we have created. I assum... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-secure-server-with-node-and-express/</link>
                <guid isPermaLink="false">66d84f98f6b5e038a1bde7e1</guid>
                
                    <category>
                        <![CDATA[ Express ]]>
                    </category>
                
                    <category>
                        <![CDATA[ node js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Security ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 14 Dec 2021 16:51:14 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/12/pexels-aleksandar-pasaric-325185.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Njoku Samson Ebere</p>
<p>In this tutorial, we will learn how to create a server. We will begin without <code>express</code> and then strengthen the server using <code>express</code>. After that, we will see how to upload images to Cloudinary from the app we have created.</p>
<p>I assume that you already understand the basics of <code>Node.js</code>, <code>express</code> and <code>nodemon</code> so we will just go straight to the practical parts.</p>
<h2 id="heading-install-nodejs-and-npm">Install Node.js and NPM</h2>
<p>If you haven't yet done so, you'll need to install Node and npm on your machine.</p>
<ol>
<li>Go to the <a target="_blank" href="https://nodejs.org/en/">Node.js website</a></li>
<li>Click on the recommended download button</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screenshot-2021-12-13-at-18.52.57.png" alt="Image" width="600" height="400" loading="lazy">
<em>Nodejs home page</em></p>
<p>When the download is complete, install Node using the downloaded <code>.exe</code> file (it follows the normal installation process).</p>
<h3 id="heading-check-if-the-installation-was-successful">Check if the installation was successful</h3>
<ol>
<li>Go to your terminal/command prompt <em>(run as administrator if possible)</em></li>
<li>Type in each of the following commands and hit Enter</li>
</ol>
<pre><code class="lang-javascript">node -v 
npm -v
</code></pre>
<p>Your output should be similar to the image below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/termial-1.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>Terminal showing the versions of node and npm</em></p>
<p>The version may be different but that's OK.</p>
<h2 id="heading-how-to-create-a-node-server-without-express">How to Create a Node Server without Express</h2>
<p>For the rest of this tutorial, I will be using VS code as my editor. You can use whatever editor you choose.</p>
<p>Let's start by creating a project directory. Open a terminal and type the following to create a directory and open it:</p>
<pre><code class="lang-javascript">
mkdir server-tutorial cd server-tutorial
</code></pre>
<p>I named my project directory <code>server-tutorial</code>, but you can name yours as you please.</p>
<p>In the terminal, type <code>npm init</code>. Hit the <code>Enter</code> button for all prompts. When completed, you should have a <code>package.json</code> file seated in your project directory.</p>
<p>The <code>package.json</code> file is just a file with all the details of your project. You don't have to open it.</p>
<p>Create a file called <code>index.js</code>.</p>
<p>In the file, require the <code>HTTP</code> module like so:</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>);
</code></pre>
<p>Call the <code>createServer()</code> method on it and assign it to a constant like this:</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">const</span> server = http.createServer();
</code></pre>
<p>Call the <code>listen()</code> method on the server constant like this:</p>
<pre><code class="lang-javascript">  server.listen();
</code></pre>
<p>Give it a port to listen to. Now this could be any free port, but we will be using port <code>3000</code> which is the conventional port. So we have this:</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>);

  <span class="hljs-keyword">const</span> server = http.createServer();

  server.listen(<span class="hljs-number">3000</span>);
</code></pre>
<p>Basically, that is all you need do to create a server.</p>
<h3 id="heading-how-to-test-the-server">How to Test the Server</h3>
<p>In your terminal (should be in the project directory), type <code>node index.js</code> and hit the <code>Enter</code> button.</p>
<p>Open a new tab in <a target="_blank" href="https://www.getpostman.com/"><code>postman</code></a> or any web <code>browser</code> and in the address bar, type <code>http://localhost:3000/</code>, and hit the <code>Enter</code> button. (I will be using postman because of its extended functionality outside the box.)</p>
<p>You will notice that your browser or postman keeps loading indefinitely like so:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/fp7k6pcfctn0548n0rmx.JPG" alt="Postman Loading Indefinitely" width="801" height="727" loading="lazy"></p>
<p>Yay! That is fine. Our Server is up and running.</p>
<p>But it's boring already. We need to make the server talk to us.</p>
<p>Let's get to it immediately.</p>
<h3 id="heading-how-to-send-back-a-response-from-the-server">How to Send Back a Response from the Server</h3>
<p>Back in the code, add the following to <code>const server = http.createServer();</code>:</p>
<pre><code class="lang-javascript"> (request, response) =&gt; {
    response.end(<span class="hljs-string">'Hey! This is your server response!'</span>);
 }
</code></pre>
<p>So we now have:</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>);

  <span class="hljs-keyword">const</span> server = http.createServer(<span class="hljs-function">(<span class="hljs-params">request, response</span>) =&gt;</span> {
    response.end(<span class="hljs-string">'Hey! This is your server response!'</span>);
  });

server.listen(<span class="hljs-number">3000</span>);
</code></pre>
<p>In basic terms, the <code>request</code> object tells the <code>server</code> that we want something, the <code>response</code> object tells us what the <code>server</code> has to say about our <code>request</code>, and the <code>end()</code> method terminates the communication with the <code>server</code> <code>response</code>.</p>
<p>Hopefully, that makes sense!</p>
<p>Now, test the server again following the steps we outlined above and your server should be talking to you. This is my output:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/b0189qzrdkppbvr57uo8.JPG" alt="Postman returning a response" width="801" height="727" loading="lazy"></p>
<p>Feel free to change the string as you wish.</p>
<p>Use <code>Control/Command + C</code> to terminate the server and run <code>node index</code> to start the server again.</p>
<p>Looking Sharp! Right? All good...</p>
<h2 id="heading-how-to-create-a-node-server-with-express">How to Create a Node Server With Express</h2>
<p>In this section, we want to make our lives easier by using <code>Express</code> and <code>Nodemon</code> (node-mon or no-demon, pronounce it as you wish).</p>
<p>In the terminal, install the following:</p>
<pre><code>npm install express --save 
npm install nodemon --save-dev
</code></pre><p>Create a new file named <code>app.js</code> or whatever suits you</p>
<p>In the file,</p>
<ol>
<li>Require express like so:</li>
</ol>
<p><code>const express = require('express');</code></p>
<ol start="2">
<li>Assign the express method to a constant like this:</li>
</ol>
<p><code>const app = express();</code></p>
<ol start="3">
<li>Export the app constant to make it available for use in other files within the directory like so:</li>
</ol>
<p><code>module.exports = app;</code></p>
<p>So we have:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);

<span class="hljs-keyword">const</span> app = express();



<span class="hljs-built_in">module</span>.exports = app;
</code></pre>
<p>In the <code>index.js</code> file, require the <code>app</code> we exported a while ago:</p>
<p><code>const app = require('./app');</code></p>
<p>Next, set the port using the app like so:</p>
<p><code>app.set('port', 3000);</code></p>
<p>And replace the code in the <code>http.createServer()</code> method with just <code>app</code> like this:</p>
<p><code>const server = http.createServer(app);</code></p>
<p>This directs all API management to the <code>app.js</code> file helping with separation of concerns.</p>
<p>So our <code>index.js</code> file now looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>);
<span class="hljs-keyword">const</span> app = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./app'</span>);

app.set(<span class="hljs-string">'port'</span>, <span class="hljs-number">3000</span>);
<span class="hljs-keyword">const</span> server = http.createServer(app);

server.listen(<span class="hljs-number">3000</span>);
</code></pre>
<p>Back in our <code>app.js</code> file, since we have directed all API management to it, let's create an endpoint to speak to us like before.</p>
<p>So before the <code>module.exports = app</code>, add the following code:</p>
<pre><code class="lang-javascript">app.use(<span class="hljs-function">(<span class="hljs-params">request, response</span>) =&gt;</span> {
   response.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Hey! This is your server response!'</span> }); 
});
</code></pre>
<p>We now have:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);

<span class="hljs-keyword">const</span> app = express();

app.use(<span class="hljs-function">(<span class="hljs-params">request, response</span>) =&gt;</span> {
   response.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Hey! This is your server response!'</span> }); 
});

<span class="hljs-built_in">module</span>.exports = app;
</code></pre>
<p>Ahaaa... It's time to test our app.</p>
<p>To test our app, we now type <code>nodemon index</code> in our terminal and hit the <code>Enter</code> button. This is my terminal:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/1gy6psmeylb6onbbt18i.JPG" alt="Terminal running nodemon" width="573" height="298" loading="lazy"></p>
<p>Do you notice that nodemon gives us details of execution in the terminal unlike Node? That's the beauty of nodemon.</p>
<p>You can now go to <code>postman</code> or any <code>browser</code> and in the address bar, type <code>http://localhost:3000/</code> and hit <code>Enter</code>. See my output:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/m4yrqaf3d235xo7hjqae.JPG" alt="Postman returning response from express app" width="804" height="727" loading="lazy"></p>
<p><strong>Walah! It's working.</strong></p>
<p>Now more reasons to use nodemon. Go to the <code>app.js</code> file and change the <code>message</code> string to any string on your choice, save, and watch the <code>terminal</code>.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/qcslu7vqa746nq5hvjwk.JPG" alt="nodemon restarting after changes" width="586" height="296" loading="lazy"></p>
<p>Wow... It automatically restarts the server. This was impossible with Node. We had to restart the server ourselves.</p>
<h2 id="heading-how-to-secure-the-server-and-make-it-future-proof">How to Secure the Server and Make it Future-Proof</h2>
<p>In the <code>index.js</code> file, replace all the code with the following:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>);
<span class="hljs-keyword">const</span> app = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./app'</span>);

<span class="hljs-keyword">const</span> normalizePort = <span class="hljs-function"><span class="hljs-params">val</span> =&gt;</span> {
  <span class="hljs-keyword">const</span> port = <span class="hljs-built_in">parseInt</span>(val, <span class="hljs-number">10</span>);

  <span class="hljs-keyword">if</span> (<span class="hljs-built_in">isNaN</span>(port)) {
    <span class="hljs-keyword">return</span> val;
  }
  <span class="hljs-keyword">if</span> (port &gt;= <span class="hljs-number">0</span>) {
    <span class="hljs-keyword">return</span> port;
  }
  <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
};
<span class="hljs-keyword">const</span> port = normalizePort(process.env.PORT || <span class="hljs-string">'3000'</span>);
app.set(<span class="hljs-string">'port'</span>, port);

<span class="hljs-keyword">const</span> errorHandler = <span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (error.syscall !== <span class="hljs-string">'listen'</span>) {
    <span class="hljs-keyword">throw</span> error;
  }
  <span class="hljs-keyword">const</span> address = server.address();
  <span class="hljs-keyword">const</span> bind = <span class="hljs-keyword">typeof</span> address === <span class="hljs-string">'string'</span> ? <span class="hljs-string">'pipe '</span> + address : <span class="hljs-string">'port: '</span> + port;
  <span class="hljs-keyword">switch</span> (error.code) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">'EACCES'</span>:
      <span class="hljs-built_in">console</span>.error(bind + <span class="hljs-string">' requires elevated privileges.'</span>);
      process.exit(<span class="hljs-number">1</span>);
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-string">'EADDRINUSE'</span>:
      <span class="hljs-built_in">console</span>.error(bind + <span class="hljs-string">' is already in use.'</span>);
      process.exit(<span class="hljs-number">1</span>);
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">default</span>:
      <span class="hljs-keyword">throw</span> error;
  }
};

<span class="hljs-keyword">const</span> server = http.createServer(app);

server.on(<span class="hljs-string">'error'</span>, errorHandler);
server.on(<span class="hljs-string">'listening'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> address = server.address();
  <span class="hljs-keyword">const</span> bind = <span class="hljs-keyword">typeof</span> address === <span class="hljs-string">'string'</span> ? <span class="hljs-string">'pipe '</span> + address : <span class="hljs-string">'port '</span> + port;
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Listening on '</span> + bind);
});

server.listen(port);
</code></pre>
<p><code>process.env.PORT</code> makes the app dynamic so that it can run any port assigned to it in the future when hosted on a live server.</p>
<p>The <code>normalizePort</code> function returns a valid port, whether it is provided as a number or a string.</p>
<p>The <code>errorHandler</code> function checks for various errors and handles them appropriately — it is then registered to the server.</p>
<p>A <code>listening</code> event listener is also registered, logging the port or named pipe on which the server is running to the console.</p>
<p>YooH! Our server is more secure and robust right now. Notice that nodemon also displays the port we are listening on.</p>
<p>There you have it, a simple, secure and robust Node.js server.</p>
<h2 id="heading-how-to-upload-images-to-cloudinary">How to Upload Images to Cloudinary</h2>
<p>Now that we have a cool server running, let's learn how we can save our images on Cloudinary. This will just be a basic introduction so it should be fun 😊.</p>
<p><a target="_blank" href="https://cloudinary.com/">Cloudinary</a> helps developers across the world manage images with minimal effort.</p>
<h3 id="heading-how-to-create-a-cloudinary-account">How to Create a Cloudinary Account</h3>
<p>To create an account, go to the <a target="_blank" href="https://cloudinary.com/">Cloudinary Website</a>.</p>
<ol>
<li>Click the <code>sign up</code> button on the <code>top right</code>.</li>
<li>Fill the form that shows up accordingly.</li>
<li>Submit the form using the <code>Create Account</code> button.</li>
<li>Check your email to finish up by validating your email</li>
</ol>
<p>You should be able to access your dashboard which looks like mine below:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/qfiw7cobdp7pv3c0fb65.JPG" alt="Cloudinary Dashboard" width="1366" height="628" loading="lazy"></p>
<p>Notice the <code>Account details</code>. You <strong>shouldn't reveal this information to anyone</strong>. I am showing it to you here because this is a temporary account that I'm using only for the purposes of this tutorial.</p>
<p>Checkout the <code>Media Library</code> tab too. This is where the uploaded images will appear.</p>
<p>If you have all these showing, then let's rock and roll...</p>
<h3 id="heading-how-to-install-cloudinary-in-our-project">How to Install Cloudinary in Our Project</h3>
<p>Open your terminal and navigate into the project directory.</p>
<p>Execute the following command to install <code>Cloudinary</code>:</p>
<pre><code class="lang-javascript">  npm install cloudinary --save
</code></pre>
<h3 id="heading-how-to-setup-cloudinary-in-our-project">How to Setup Cloudinary in Our Project</h3>
<p>In the app.js file, require <code>cloudinary</code> below the <code>const app = express();</code> like so:</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">const</span> cloudinary = <span class="hljs-built_in">require</span>(<span class="hljs-string">'cloudinary'</span>).v2
</code></pre>
<p>Next, add the configuration details from the account details on your dashboard like this:</p>
<pre><code class="lang-javascript">    cloud_name: <span class="hljs-string">'place your cloud_name here'</span>,
    <span class="hljs-attr">api_key</span>: <span class="hljs-string">'place your api_key here'</span>,
    <span class="hljs-attr">api_secret</span>: <span class="hljs-string">'place your api_secret here'</span>,
</code></pre>
<p>This is what I have:</p>
<pre><code class="lang-javascript">  <span class="hljs-comment">// cloudinary configuration</span>
  cloudinary.config({
    <span class="hljs-attr">cloud_name</span>: <span class="hljs-string">"dunksyqjj"</span>,
    <span class="hljs-attr">api_key</span>: <span class="hljs-string">"173989938887513"</span>,
    <span class="hljs-attr">api_secret</span>: <span class="hljs-string">"ZPLqvCzRu55MaM1rt-wxJCmkxqU"</span>
  });
</code></pre>
<h3 id="heading-how-to-create-an-endpoint-to-upload-an-image">How to Create an EndPoint to Upload an Image</h3>
<p>To avoid bugs in our code, first replace the existing API with the following code:</p>
<pre><code class="lang-javascript">  app.get(<span class="hljs-string">"/"</span>, <span class="hljs-function">(<span class="hljs-params">request, response</span>) =&gt;</span> {
    response.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Hey! This is your server response!"</span> });
  });
</code></pre>
<p>It is basically the same but this time, we are using the <code>get</code> verb in place of the <code>use</code> verb and we added a root end-point (<code>/</code>).</p>
<p>Next, just before the <code>module.exports = app;</code> line, we will be creating our <code>image-upload</code> API.</p>
<p>Let's start by placing this code there:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// image upload API</span>
app.post(<span class="hljs-string">"/upload-image"</span>, <span class="hljs-function">(<span class="hljs-params">request, response</span>) =&gt;</span> {});
</code></pre>
<p>Basically, this is how we set up an API. The API makes a <code>POST</code> <code>request</code> to the <code>server</code> telling the <code>server</code> that the <code>request</code> should be handled with a degree of security. It uses two parameters to make this request: an <code>end-point</code> (/upload-image) and a <code>callback function</code> ((request, response) =&gt; {}).</p>
<p>Let's breathe life into the API by building out the <code>callback function</code>.</p>
<h3 id="heading-how-to-build-the-callback-function">How to Build the Callback Function</h3>
<h4 id="heading-install-body-parserhttpswwwnpmjscompackagebody-parser">Install <a target="_blank" href="https://www.npmjs.com/package/body-parser">body-parser</a></h4>
<p>This npm package enables us to handle incoming requests using <code>req.body</code> or <code>request.body</code> as the case may be. We will be installing <code>body-parser</code> using the following code:</p>
<pre><code class="lang-javascript">  npm install --save body-parser
</code></pre>
<h4 id="heading-configure-body-paser-for-our-project">Configure body-paser for our project</h4>
<p>Require body-parse in the app.js like so:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> bodyParser = <span class="hljs-built_in">require</span>(<span class="hljs-string">'body-parser'</span>);
</code></pre>
<p>Next, add the following code to set its <code>json</code> function as global middleware for our app like so:</p>
<pre><code class="lang-javascript">  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({ <span class="hljs-attr">extended</span>: <span class="hljs-literal">true</span> }));
</code></pre>
<p>We can now handle our request body appropriately.</p>
<h3 id="heading-back-to-building-our-function">Back to Building Our Function</h3>
<p>In the function, add the following code to collect any data (images) entered by a user:</p>
<pre><code class="lang-javascript">    <span class="hljs-comment">// collected image from a user</span>
    <span class="hljs-keyword">const</span> data = {
        <span class="hljs-attr">image</span>: request.body.image,
    };
</code></pre>
<p>Next, upload the image to <code>cloudinary</code> using the following code:</p>
<pre><code class="lang-javascript">cloudinary.uploader.upload(data.image);
</code></pre>
<p>Basically, this is all we need to upload our image. So our <code>app.js</code> looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> cloudinary = <span class="hljs-built_in">require</span>(<span class="hljs-string">"cloudinary"</span>).v2;
<span class="hljs-keyword">const</span> bodyParser = <span class="hljs-built_in">require</span>(<span class="hljs-string">'body-parser'</span>);

<span class="hljs-comment">// body parser configuration</span>
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ <span class="hljs-attr">extended</span>: <span class="hljs-literal">true</span> }));

<span class="hljs-comment">// cloudinary configuration</span>
cloudinary.config({
  <span class="hljs-attr">cloud_name</span>: <span class="hljs-string">"dunksyqjj"</span>,
  <span class="hljs-attr">api_key</span>: <span class="hljs-string">"173989938887513"</span>,
  <span class="hljs-attr">api_secret</span>: <span class="hljs-string">"ZPLqvCzRu55MaM1rt-wxJCmkxqU"</span>
});

app.get(<span class="hljs-string">"/"</span>, <span class="hljs-function">(<span class="hljs-params">request, response</span>) =&gt;</span> {
  response.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Hey! This is your server response!"</span> });
});

<span class="hljs-comment">// image upload API</span>
app.post(<span class="hljs-string">"/image-upload"</span>, <span class="hljs-function">(<span class="hljs-params">request, response</span>) =&gt;</span> {
    <span class="hljs-comment">// collected image from a user</span>
    <span class="hljs-keyword">const</span> data = {
      <span class="hljs-attr">image</span>: request.body.image,
    }

    <span class="hljs-comment">// upload image here</span>
    cloudinary.uploader.upload(data.image);

});

<span class="hljs-built_in">module</span>.exports = app;
</code></pre>
<p>Now this looks good and it works perfectly. You can test it out using <code>postman</code>. But it's would be awesome if our app could give us feedback when it's done handling our requests, right?</p>
<p>To make this happen, we will add the following <code>then...catch...</code> block to the cloudinary upload like so:</p>
<pre><code class="lang-javascript">    <span class="hljs-comment">// upload image here</span>
    cloudinary.uploader.upload(data.image)
    .then(<span class="hljs-function">(<span class="hljs-params">result</span>) =&gt;</span> {
      response.status(<span class="hljs-number">200</span>).send({
        <span class="hljs-attr">message</span>: <span class="hljs-string">"success"</span>,
        result,
      });
    }).catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
      response.status(<span class="hljs-number">500</span>).send({
        <span class="hljs-attr">message</span>: <span class="hljs-string">"failure"</span>,
        error,
      });
    });
</code></pre>
<p>So our final code will be:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> cloudinary = <span class="hljs-built_in">require</span>(<span class="hljs-string">"cloudinary"</span>).v2;
<span class="hljs-keyword">const</span> bodyParser = <span class="hljs-built_in">require</span>(<span class="hljs-string">'body-parser'</span>);

<span class="hljs-comment">// body parser configuration</span>
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ <span class="hljs-attr">extended</span>: <span class="hljs-literal">true</span> }));

<span class="hljs-comment">// cloudinary configuration</span>
cloudinary.config({
  <span class="hljs-attr">cloud_name</span>: <span class="hljs-string">"dunksyqjj"</span>,
  <span class="hljs-attr">api_key</span>: <span class="hljs-string">"173989938887513"</span>,
  <span class="hljs-attr">api_secret</span>: <span class="hljs-string">"ZPLqvCzRu55MaM1rt-wxJCmkxqU"</span>
});

app.get(<span class="hljs-string">"/"</span>, <span class="hljs-function">(<span class="hljs-params">request, response</span>) =&gt;</span> {
  response.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Hey! This is your server response!"</span> });
});

<span class="hljs-comment">// image upload API</span>
app.post(<span class="hljs-string">"/image-upload"</span>, <span class="hljs-function">(<span class="hljs-params">request, response</span>) =&gt;</span> {
    <span class="hljs-comment">// collected image from a user</span>
    <span class="hljs-keyword">const</span> data = {
      <span class="hljs-attr">image</span>: request.body.image,
    }

    <span class="hljs-comment">// upload image here</span>
    cloudinary.uploader.upload(data.image)
    .then(<span class="hljs-function">(<span class="hljs-params">result</span>) =&gt;</span> {
      response.status(<span class="hljs-number">200</span>).send({
        <span class="hljs-attr">message</span>: <span class="hljs-string">"success"</span>,
        result,
      });
    }).catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
      response.status(<span class="hljs-number">500</span>).send({
        <span class="hljs-attr">message</span>: <span class="hljs-string">"failure"</span>,
        error,
      });
    });

});

<span class="hljs-built_in">module</span>.exports = app;
</code></pre>
<h3 id="heading-how-to-test-our-api">How to Test our API</h3>
<p>Create a folder/directory in the root directory and name it <code>images</code> like so:</p>
<pre><code class="lang-javascript">  mkdir images
</code></pre>
<p>Copy an image of your choice to this folder. (Now, the path to your image relative to the app.js file should look like this: <code>"images/&lt;your-image.jpg"&gt;</code>.)</p>
<p>Now let's proceed to <code>postman</code>.</p>
<ol>
<li>In the address bar, enter this: <code>http://localhost:3000/image-upload</code></li>
<li>Set the <code>Header</code> Key to <code>Content-Type</code> and value to <code>application/json</code></li>
<li>Set the <code>body</code> to the <code>json</code> data we declared in our code like so:</li>
</ol>
<pre><code class="lang-javascript">       {
       <span class="hljs-string">"image"</span>: <span class="hljs-string">"images/oskar-yildiz-gy08FXeM2L4-unsplash.jpg"</span>
       }
</code></pre>
<p>Hit the <code>Send</code> button and wait for the upload to complete and get your response:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/ate77jhmka2agj3nxip2.JPG" alt="Postman setup to upload image" width="1366" height="729" loading="lazy"></p>
<p>Now, this is the result. The image now has a unique <code>public_id</code> which is randomly generated by Cloudinary and a <code>secure_url</code> which is globally accessible (you can load it in your browser to see).</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/wys21hgc58nl4rfmq63i.JPG" alt="Postman showing result of upload" width="1366" height="730" loading="lazy"></p>
<p>Finally, checking the <code>Media Library</code> tab on your Cloudinary dashboard, you should have a new image with a <code>new</code> badge on it. This will have a unique id that matches the <code>public_id</code> we saw in the postman result above just like in the image below:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/vkvevngtumyfdd105suu.JPG" alt="Cloudinary Media Files" width="1366" height="627" loading="lazy"></p>
<p>Walah! We are persisting images without stress...That feels good.</p>
<p>Well, one more thing – SECURITY!</p>
<p>Our Cloudinary configuration details rea exposed in our app.js file. If we push our project to GitHub, it becomes publicly available to anyone who cares to check it out. And that becomes a problem if it gets into the wrong hands.</p>
<p>But don't worry about a thing here, there is a fix for almost everything in this space. We will be using the <code>dotenv</code> npm package to hid our configurations from the public.</p>
<h2 id="heading-how-to-secure-our-configurations-with-dotenv">How to Secure Our Configurations with <code>dotenv</code></h2>
<p>First, you'll need to install <a target="_blank" href="https://www.npmjs.com/package/dotenv">dotenv</a> if you haven't already:</p>
<pre><code class="lang-javascript">npm install dotenv --save
</code></pre>
<p>Then require <code>dotenv</code> in <code>app.js</code> like so:</p>
<pre><code class="lang-javascript">  <span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config()
</code></pre>
<p>Create a new file in the root directory and name it <code>.env</code>.</p>
<p>In the file, enter your Cloudinary configuration details like so:</p>
<pre><code class="lang-javascript">  CLOUD_NAME=dunksyqjj
  API_KEY=<span class="hljs-number">173989938887513</span>
  API_SECRET=ZPLqvCzRu55MaM1rt-wxJCmkxqU
</code></pre>
<p>In the app.js file, we will access the configurations in the <code>.env</code> file via the <code>process.env</code> property like so:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// cloudinary configuration</span>
cloudinary.config({
  <span class="hljs-attr">cloud_name</span>: process.env.CLOUD_NAME,
  <span class="hljs-attr">api_key</span>: process.env.API_KEY,
  <span class="hljs-attr">api_secret</span>: process.env.API_SECRET
});
</code></pre>
<p>This is my <code>app.js</code> code at the moment:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> cloudinary = <span class="hljs-built_in">require</span>(<span class="hljs-string">"cloudinary"</span>).v2;
<span class="hljs-keyword">const</span> bodyParser = <span class="hljs-built_in">require</span>(<span class="hljs-string">'body-parser'</span>);
<span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config()

<span class="hljs-comment">// body parser configuration</span>
app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({ <span class="hljs-attr">extended</span>: <span class="hljs-literal">true</span> }));

<span class="hljs-comment">// cloudinary configuration</span>
cloudinary.config({
  <span class="hljs-attr">cloud_name</span>: process.env.CLOUD_NAME,
  <span class="hljs-attr">api_key</span>: process.env.API_KEY,
  <span class="hljs-attr">api_secret</span>: process.env.API_SECRET
});

app.get(<span class="hljs-string">"/"</span>, <span class="hljs-function">(<span class="hljs-params">request, response, next</span>) =&gt;</span> {
  response.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Hey! This is your server response!"</span> });
  next();
});

<span class="hljs-comment">// image upload API</span>
app.post(<span class="hljs-string">"/image-upload"</span>, <span class="hljs-function">(<span class="hljs-params">request, response</span>) =&gt;</span> {
    <span class="hljs-comment">// collected image from a user</span>
    <span class="hljs-keyword">const</span> data = {
      <span class="hljs-attr">image</span>: request.body.image,
    }

    <span class="hljs-comment">// upload image here</span>
    cloudinary.uploader.upload(data.image)
    .then(<span class="hljs-function">(<span class="hljs-params">result</span>) =&gt;</span> {
      response.status(<span class="hljs-number">200</span>).send({
        <span class="hljs-attr">message</span>: <span class="hljs-string">"success"</span>,
        result,
      });
    }).catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
      response.status(<span class="hljs-number">500</span>).send({
        <span class="hljs-attr">message</span>: <span class="hljs-string">"failure"</span>,
        error,
      });
    });
});

<span class="hljs-built_in">module</span>.exports = app;
</code></pre>
<p>Let's test our app again to make sure that nothing is broken. Here is my result:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/d3a2b47shxlmext6o40j.JPG" alt="Cloudinary media library" width="1366" height="627" loading="lazy"></p>
<p>I now have two of the same image but with different <code>public_id</code>s.</p>
<p>And that is it!</p>
<p>Yeeeh! Our application is more secure than it was when we started.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, we went through the steps involved in creating a server using just Node.js. And after that, we improved our server using Express and nodemon.</p>
<p>Finally, we saw how to upload an image to Cloudinary through our Express application and how our configuration details are secured using the <code>dotenv</code> package.</p>
<p>This is just an introduction. You can do a whole lot more if you play around with this application.</p>
<p>You can find the server creation code <a target="_blank" href="https://github.com/EBEREGIT/server-tutorial/tree/create-server">here</a>.</p>
<p>And the image upload codes are available <a target="_blank" href="https://github.com/EBEREGIT/server-tutorial/tree/cloudinary-upload">here</a>.</p>
<p>Thank you for your time. 😊</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Secure Linux Servers with SE Linux ]]>
                </title>
                <description>
                    <![CDATA[ Security is an extremely important aspect of software development, server management, and application development these days.  And if you use Linux, you're in luck – it comes with an excellent feature called SE Linux that helps you add an additional ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/securing-linux-servers-with-se-linux/</link>
                <guid isPermaLink="false">66adea746778e7bd69427bc0</guid>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Zaira Hira ]]>
                </dc:creator>
                <pubDate>Tue, 26 Oct 2021 17:33:34 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/10/Enhance-Linux-server-security-with-SE-Linux--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Security is an extremely important aspect of software development, server management, and application development these days. </p>
<p>And if you use Linux, you're in luck – it comes with an excellent feature called SE Linux that helps you add an additional layer of security.</p>
<h2 id="heading-what-is-se-linux">What is SE Linux?</h2>
<p>SE Linux was developed by the NSA (National Security Agency) to serve government-related security tasks.  </p>
<p>SE Linux stands for Security Enhanced Linux. It gives system administrators more control for providing access to files and processes. With SE Linux, admins can define a context and tag files and permit them within that context.</p>
<p>Access and permissions are usually inherited based on user groups. But there are some sources (like web servers and processes) that need access to network ports and kernel processes. These are usually spawned by the root user or users with special access. SE Linux helps you limit who can access these special processes.</p>
<h2 id="heading-how-to-work-with-se-linux">How to work with SE Linux</h2>
<p>SE Linux comes by default in most Linux distros. In this post, I will be working on Fedora. </p>
<p>Just keep in mind that working with SE Linux requires <code>sudo</code> or <code>root</code> access.</p>
<p>The SE Linux configuration file is located in the <code>/etc/sysconfig/selinux</code> folder. Let's view its contents:</p>
<h3 id="heading-se-linux-modes">SE Linux Modes</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/image-69.png" alt="Image" width="600" height="400" loading="lazy">
<em>SE Linux config file</em></p>
<p>In the config file, we can change the modes and choose any one from the below:</p>
<ol>
<li><strong>Enforced</strong> – Enabled by default, filters based on defined policies.</li>
<li><strong>Permissive</strong> – Does not enforce the defined policies, but records all of the attempts in log files. This mode is useful for troubleshooting.</li>
<li><strong>Disabled</strong> – SE Linux is completely disabled. This is not recommended as it might expose your system to threats. Also, reverting back to enforced could create certain discrepancies.</li>
</ol>
<p>You can check your current SE Linux mode with the below commands:</p>
<ol>
<li><code>getenforce</code></li>
<li><code>sestatus</code></li>
</ol>
<p>If you only need to change the mode for the current session, you can use the below commands: </p>
<ol>
<li><code>sudo setenforce 0</code> – sets permissive mode for current session</li>
<li><code>sudo setenforce 1</code> – sets enforcing mode for current session</li>
</ol>
<h2 id="heading-se-linux-policies">SE Linux Policies</h2>
<p>In SE Linux, policies define access to users. Users define access to roles and roles define access to domains. Domains then provide access to certain files.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/image-61.png" alt="Image" width="600" height="400" loading="lazy">
<em>SE Linux Policies</em></p>
<p>To change and modify accesses, 'booleans' are defined. We'll look into booleans in the next section.</p>
<h3 id="heading-how-to-manage-se-linux-policies-with-booleans">How to manage SE Linux policies with booleans</h3>
<p>As you now know, SE Linux policies are managed by booleans.</p>
<p>Let's see a working example of how you'd view and set a boolean. In this example, we will set booleans specific to <code>httpd</code>.</p>
<p>First, list all modules specific to http – <code>getsebool -a | grep httpd</code>.</p>
<p><em>Here <strong><code>-a</code></strong> lists all booleans.</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/image-80.png" alt="Image" width="600" height="400" loading="lazy">
<em>List of Booleans</em></p>
<p>Next, let's select and change the yellow highlighted boolean in the code above:</p>
<pre><code class="lang-bash">getsebool httpd_can_connect_ftp
</code></pre>
<p>Now, set the value to <code>allow</code>.</p>
<pre><code class="lang-bash">setsebool -P httpd_can_connect_ftp 1
</code></pre>
<p>In the command above,</p>
<ul>
<li>The flag P is used to make the change permanent even after reboot.</li>
<li><code>1</code> is enabling the boolean.</li>
</ul>
<p>Now, when you list the process again, its value will be allowed.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/image-81.png" alt="Image" width="600" height="400" loading="lazy">
<em>Boolean 'on'</em></p>
<h2 id="heading-se-linux-architecture">SE Linux Architecture</h2>
<p>The diagram below explains how SE Linux validates an attempt from source:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/image-62.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-troubleshooting-and-se-linux-logs">Troubleshooting and SE Linux Logs</h2>
<p>SE Linux produces very detailed logs for each attempt. You can find the logs and view them here: <code>/var/log/audit</code>.</p>
<p>While troubleshooting, you should move into 'permissive' mode so all events can be recorded in the logs. Although policies are not being enforced, attempts are being recorded in logs.</p>
<h2 id="heading-how-to-disable-and-enable-se-linux">How to disable and enable SE Linux</h2>
<p>Disabling SE Linux completely is never a good option. But there are certain scenarios where policies can be by-passed such as when troubleshooting. </p>
<p>Instead of disabling SE Linux if you run into a small issued, it is better to invest some time in troubleshooting. </p>
<p>But if you really need to disable SE Linux, follow these steps: </p>
<ol>
<li>Change the mode from 'enforcing' to 'permissive'.</li>
<li>Change mode from 'permissive' to 'disabled'.</li>
</ol>
<h2 id="heading-wrapping-up">Wrapping up</h2>
<p>Learning SE Linux is worth your time and there are endless possibilities you can explore when using it. </p>
<p>SE Linux provides admins with a fine-grained level of control. So why not learn it and leverage it to increase your security?</p>
<p>Thanks for reading until the end. Let's connect on <a target="_blank" href="https://twitter.com/hira_zaira">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The NGINX Handbook – Learn NGINX for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ A young Russian developer named Igor Sysoev was frustrated by older web servers' inability to handle more than 10 thousand concurrent requests. This is a problem referred to as the C10k problem. As an answer to this, he started working on a new web s... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-nginx-handbook/</link>
                <guid isPermaLink="false">66b0ab588d675d0da5f1ab85</guid>
                
                    <category>
                        <![CDATA[ nginx ]]>
                    </category>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Farhan Hasin Chowdhury ]]>
                </dc:creator>
                <pubDate>Mon, 26 Apr 2021 23:56:38 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/07/NGINX-Handbook-Mockup.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A young Russian developer named <a target="_blank" href="https://en.wikipedia.org/wiki/Igor_Sysoev">Igor Sysoev</a> was frustrated by older web servers' inability to handle more than 10 thousand concurrent requests. This is a problem referred to as the <a target="_blank" href="https://en.wikipedia.org/wiki/C10k_problem">C10k problem</a>. As an answer to this, he started working on a new web server back in 2002.</p>
<p><a target="_blank" href="https://nginx.org/">NGINX</a> was first released to the public in 2004 under the terms of the <a target="_blank" href="https://en.wikipedia.org/wiki/2-clause_BSD">2-clause BSD</a> license. According to the <a target="_blank" href="https://news.netcraft.com/archives/2021/03/29/march-2021-web-server-survey.html">March 2021 Web Server Survey</a>, NGINX holds 35.3% of the market with a total of 419.6 million sites.</p>
<p>Thanks to tools like <a target="_blank" href="https://www.digitalocean.com/community/tools/nginx">NGINXConfig</a> by <a target="_blank" href="https://digitalocean.com/">DigitalOcean</a> and an abundance of pre-written configuration files on the internet, people tend to do a lot of copy-pasting instead of trying to understand when it comes to configuring NGINX.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/177962736_1410222585999736_5618677227291897851_n.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Trust me, it's not that hard...</em></p>
<p>I'm not saying that copying code is bad, but copying code without understanding is a big "no no". </p>
<p>Also NGINX is the kind of software that should be configured exactly according to the requirements of the application to be served and available resources on the host. </p>
<p>That's why instead of copying blindly, you should understand and then fine tune what you're copying – and that's where this handbook comes in.</p>
<p>After going through the entire book, you should be able to:</p>
<ul>
<li>Understand configuration files generated by popular tools as well as those found in various documentation.</li>
<li>Configure NGINX as a web server, a reverse proxy server, and a load balancer from scratch.</li>
<li>Optimize NGINX to get maximum performance out of your server.</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li>Familiarity with the Linux terminal and common Unix programs such as <code>ls</code>, <code>cat</code>, <code>ps</code>, <code>grep</code>, <code>find</code>, <code>nproc</code>, <code>ulimit</code> and <code>nano</code>.</li>
<li>A computer powerful enough to run a virtual machine or a $5 virtual private server.</li>
<li>Understanding of web applications and a programming language such as JavaScript or PHP.</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-introduction-to-nginx">Introduction to NGINX</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-nginx">How to Install NGINX</a><ul>
<li><a class="post-section-overview" href="#heading-how-to-provision-a-local-virtual-machine">How to Provision a Local Virtual Machine</a></li>
<li><a class="post-section-overview" href="#heading-how-to-provision-a-virtual-private-server">How to Provision a Virtual Private Server</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-nginx-on-a-provisioned-server-or-virtual-machine-2">How to Install NGINX on a Provisioned Server or Virtual Machine</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-introduction-to-nginxs-configuration-files">Introduction to NGINX's Configuration Files</a></li>
<li><a class="post-section-overview" href="#heading-how-to-configure-a-basic-web-server">How to Configure a Basic Web Server</a><ul>
<li><a class="post-section-overview" href="#heading-how-to-write-your-first-configuration-file">How to Write Your First Configuration File</a></li>
<li><a class="post-section-overview" href="#heading-how-to-validate-and-reload-configuration-files">How to Validate and Reload Configuration Files</a></li>
<li><a class="post-section-overview" href="#heading-how-to-understand-directives-and-contexts-in-nginx">How to Understand Directives and Contexts in NGINX</a></li>
<li><a class="post-section-overview" href="#heading-how-to-serve-static-content-using-nginx">How to Serve Static Content Using NGINX</a></li>
<li><a class="post-section-overview" href="#heading-static-file-type-handling-in-nginx">Static File Type Handling in NGINX</a></li>
<li><a class="post-section-overview" href="#heading-how-to-include-partial-config-files">How to Include Partial Config Files</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-dynamic-routing-in-nginx">Dynamic Routing in NGINX</a><ul>
<li><a class="post-section-overview" href="#heading-location-matches">Location Matches</a></li>
<li><a class="post-section-overview" href="#heading-variables-in-nginx">Variables in NGINX</a></li>
<li><a class="post-section-overview" href="#heading-redirects-and-rewrites">Redirects and Rewrites</a></li>
<li><a class="post-section-overview" href="#heading-how-to-try-for-multiple-files">How to Try for Multiple Files</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-logging-in-nginx">Logging in NGINX</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-nginx-as-a-reverse-proxy">How to Use NGINX as a Reverse Proxy</a><ul>
<li><a class="post-section-overview" href="#heading-nodejs-with-nginx">Node.js With NGINX</a></li>
<li><a class="post-section-overview" href="#heading-php-with-nginx">PHP With NGINX</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-how-to-use-nginx-as-a-load-balancer">How to Use NGINX as a Load Balancer</a></li>
<li><a class="post-section-overview" href="#heading-how-to-optimize-nginx-for-maximum-performance">How To Optimize NGINX for Maximum Performance</a><ul>
<li><a class="post-section-overview" href="#heading-how-to-configure-worker-processes-and-worker-connections">How to Configure Worker Processes and Worker Connections</a></li>
<li><a class="post-section-overview" href="#heading-how-to-cache-static-content">How to Cache Static Content</a></li>
<li><a class="post-section-overview" href="#heading-how-to-compress-responses">How to Compress Responses</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-how-to-understand-the-main-configuration-file-1">How to Understand the Main Configuration File</a></li>
<li><a class="post-section-overview" href="#heading-how-to-configure-ssl-and-http2">How To Configure SSL and HTTP/2</a><ul>
<li><a class="post-section-overview" href="#heading-how-to-configure-ssl">How To Configure SSL</a></li>
<li><a class="post-section-overview" href="#heading-how-to-enable-http2">How to Enable HTTP/2</a></li>
<li><a class="post-section-overview" href="#heading-how-to-enable-server-push">How to Enable Server Push</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-project-code">Project Code</h2>
<p>You can find the code for the example projects in the following repository:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/fhsinchy/nginx-handbook-projects">https://github.com/fhsinchy/nginx-handbook-projects</a></div>
<h2 id="heading-introduction-to-nginx">Introduction to NGINX</h2>
<p><a target="_blank" href="https://nginx.org/">NGINX</a> is a high performance web server developed to facilitate the increasing needs of the modern web. It focuses on high performance, high concurrency, and low resource usage. Although it's mostly known as a web server, NGINX at its core is a <a target="_blank" href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a> server.</p>
<p>NGINX is not the only web server on the market, though. One of its biggest competitors is <a target="_blank" href="https://httpd.apache.org/">Apache HTTP Server (httpd)</a>, first released back on 1995. In spite of the fact that Apache HTTP Server is more flexible, server admins often prefer NGINX for two main reasons:</p>
<ul>
<li>It can handle a higher number of concurrent requests.</li>
<li>It has faster static content delivery with low resource usage.</li>
</ul>
<p>I won't go further into the whole Apache vs NGINX debate. But if you wish to learn more about the differences between them in detail, this excellent <a target="_blank" href="https://www.digitalocean.com/community/tutorials/apache-vs-nginx-practical-considerations">article</a> from <a target="_blank" href="https://www.digitalocean.com/community/users/jellingwood">Justin Ellingwood</a> may help.</p>
<p>In fact, to explain NGINX's request handling technique, I would like to quote two paragraphs from Justin's article here:</p>
<blockquote>
<p>Nginx came onto the scene after Apache, with more awareness of the concurrency problems that would face sites at scale. Leveraging this knowledge, Nginx was designed from the ground up to use an asynchronous, non-blocking, event-driven connection handling algorithm.  </p>
<p>Nginx spawns worker processes, each of which can handle thousands of connections. The worker processes accomplish this by implementing a fast looping mechanism that continuously checks for and processes events. Decoupling actual work from connections allows each worker to concern itself with a connection only when a new event has been triggered.</p>
</blockquote>
<p>If that seems a bit complicated to understand, don't worry. Having a basic understanding of the inner workings will suffice for now.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/wQszK2rvq-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>NGINX is faster in static content delivery while staying relatively lighter on resources because it doesn't embed a dynamic programming language processor. When a request for static content comes, NGINX simply responds with the file without running any additional processes.</p>
<p>That doesn't mean that NGINX can't handle requests that require a dynamic programming language processor. In such cases, NGINX simply delegates the tasks to separate processes such as <a target="_blank" href="https://www.php.net/manual/en/install.fpm.php">PHP-FPM</a>, <a target="_blank" href="https://nodejs.org/">Node.js</a> or <a target="_blank" href="https://python.org/">Python</a>. Then, once that process finishes its work, NGINX reverse proxies the response back to the client.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/_nT7rcdjG.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>NGINX is also a lot easier to configure thanks to a configuration file syntax inspired from various scripting languages that results in compact, easily maintainable configuration files.</p>
<h2 id="heading-how-to-install-nginx">How to Install NGINX</h2>
<p>Installing NGINX on a <a target="_blank" href="https://en.wikipedia.org/wiki/Linux">Linux</a>-based system is pretty straightforward. You can either use a virtual private server running <a target="_blank" href="https://ubuntu.com/">Ubuntu</a> as your playground, or you can provision a virtual machine on your local system using Vagrant.</p>
<p>For the most part, provisioning a local virtual machine will suffice and that's the way I'll be using in this article.</p>
<h3 id="heading-how-to-provision-a-local-virtual-machine">How to Provision a Local Virtual Machine</h3>
<p>For those who doesn't know, <a target="_blank" href="https://vagrantup.com/">Vagrant</a> is an open-source tool by <a target="_blank" href="https://www.hashicorp.com/">Hashicorp</a> that allows you to provision virtual machines using simple configuration files.</p>
<p>For this approach to work, you'll need <a target="_blank" href="https://www.virtualbox.org/wiki/Downloads/">VirtualBox</a> and <a target="_blank" href="https://www.vagrantup.com/downloads/">Vagrant</a>, so go ahead and install them first. If you need a little warm up on the topic, this <a target="_blank" href="https://learn.hashicorp.com/collections/vagrant/getting-started/">tutorial</a> may help.</p>
<p>Create a working directory somewhere in your system with a sensible name. Mine is <code>~/vagrant/nginx-handbook</code> directory.</p>
<p>Inside the working directory create a file named <code>Vagrantfile</code> and put following content in there:</p>
<pre><code class="lang-vagrantfile">Vagrant.configure("2") do |config|

    config.vm.hostname = "nginx-handbook-box"

    config.vm.box = "ubuntu/focal64"

    config.vm.define "nginx-handbook-box"

    config.vm.network "private_network", ip: "192.168.20.20"

    config.vm.provider "virtualbox" do |vb|
      vb.cpus = 1
      vb.memory = "1024"
      vb.name = "nginx-handbook"
    end

  end
</code></pre>
<p>This <code>Vagrantfile</code> is the configuration file I talked about earlier. It contains information like name of the virtual machine, number of CPUs, size of RAM, the IP address, and more.</p>
<p>To start a virtual machine using this configuration, open your terminal inside the working directory and execute the following command:</p>
<pre><code class="lang-shell">vagrant up

# Bringing machine 'nginx-handbook-box' up with 'virtualbox' provider...
# ==&gt; nginx-handbook-box: Importing base box 'ubuntu/focal64'...
# ==&gt; nginx-handbook-box: Matching MAC address for NAT networking...
# ==&gt; nginx-handbook-box: Checking if box 'ubuntu/focal64' version '20210415.0.0' is up to date...
# ==&gt; nginx-handbook-box: Setting the name of the VM: nginx-handbook
# ==&gt; nginx-handbook-box: Clearing any previously set network interfaces...
# ==&gt; nginx-handbook-box: Preparing network interfaces based on configuration...
#     nginx-handbook-box: Adapter 1: nat
#     nginx-handbook-box: Adapter 2: hostonly
# ==&gt; nginx-handbook-box: Forwarding ports...
#     nginx-handbook-box: 22 (guest) =&gt; 2222 (host) (adapter 1)
# ==&gt; nginx-handbook-box: Running 'pre-boot' VM customizations...
# ==&gt; nginx-handbook-box: Booting VM...
# ==&gt; nginx-handbook-box: Waiting for machine to boot. This may take a few minutes...
#     nginx-handbook-box: SSH address: 127.0.0.1:2222
#     nginx-handbook-box: SSH username: vagrant
#     nginx-handbook-box: SSH auth method: private key
#     nginx-handbook-box: Warning: Remote connection disconnect. Retrying...
#     nginx-handbook-box: Warning: Connection reset. Retrying...
#     nginx-handbook-box: 
#     nginx-handbook-box: Vagrant insecure key detected. Vagrant will automatically replace
#     nginx-handbook-box: this with a newly generated keypair for better security.
#     nginx-handbook-box: 
#     nginx-handbook-box: Inserting generated public key within guest...
#     nginx-handbook-box: Removing insecure key from the guest if it's present...
#     nginx-handbook-box: Key inserted! Disconnecting and reconnecting using new SSH key...
# ==&gt; nginx-handbook-box: Machine booted and ready!
# ==&gt; nginx-handbook-box: Checking for guest additions in VM...
# ==&gt; nginx-handbook-box: Setting hostname...
# ==&gt; nginx-handbook-box: Configuring and enabling network interfaces...
# ==&gt; nginx-handbook-box: Mounting shared folders...
#     nginx-handbook-box: /vagrant =&gt; /home/fhsinchy/vagrant/nginx-handbook

vagrant status

# Current machine states:

# nginx-handbook-box           running (virtualbox)
</code></pre>
<p>The output of the <code>vagrant up</code> command may differ on your system, but as long as <code>vagrant status</code> says the machine is running, you're good to go. </p>
<p>Given that the virtual machine is now running, you should be able to SSH into it. To do so, execute the following command:</p>
<pre><code class="lang-shell">vagrant ssh nginx-handbook-box

# Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-72-generic x86_64)
# vagrant@nginx-handbook-box:~$
</code></pre>
<p>If everything's done correctly you should be logged into your virtual machine, which will be evident by the <code>vagrant@nginx-handbook-box</code> line on your terminal. </p>
<p>This virtual machine will be accessible on <strong>http://192.168.20.20</strong> on your local machine. You can even assign a custom domain like <strong>http://nginx-handbook.test</strong> to the virtual machine by adding an entry to your <strong>hosts</strong> file:</p>
<pre><code class="lang-shell"># on mac and linux terminal
sudo nano /etc/hosts

# on windows command prompt as administrator
notepad c:\windows\system32\drivers\etc\hosts
</code></pre>
<p>Now append the following line at the end of the file:</p>
<pre><code><span class="hljs-number">192.168</span><span class="hljs-number">.20</span><span class="hljs-number">.20</span>   nginx-handbook.test
</code></pre><p>Now you should be able to access the virtual machine on <strong>http://nginx-handbook.test</strong> URI in your browser.</p>
<p>You can stop or destroy the virtual machine by executing the following commands inside the working directory:</p>
<pre><code class="lang-shell"># to stop the virtual machine
vagrant halt

# to destroy the virtual machine
vagrant destroy
</code></pre>
<p>If you want to learn about more Vagrant commands, this <a target="_blank" href="https://gist.github.com/wpscholar/a49594e2e2b918f4d0c4">cheat sheet</a> may come in handy.</p>
<p>Now that you have a functioning Ubuntu virtual machine on your system, all that is left to do is <a class="post-section-overview" href="#heading-how-to-install-nginx-on-a-provisioned-server-or-virtual-machine-2">install NGINX</a>.</p>
<h3 id="heading-how-to-provision-a-virtual-private-server">How to Provision a Virtual Private Server</h3>
<p>For this demonstration, I'll use <a target="_blank" href="https://vultr.com/">Vultr</a> as my provider but you may use <a target="_blank" href="https://digitalocean.com/">DigitalOcean</a> or whatever provider you like.</p>
<p>Assuming you already have an account with your provider, log into the account and deploy a new server:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/ZUAu_Tpxx-2.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>On DigitalOcean, it's usually called a droplet. On the next screen, choose a location close to you. I live in Bangladesh which is why I've chosen Singapore:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/zH08EnmGq.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>On the next step, you'll have to choose the operating system and server size. Choose Ubuntu 20.04 and the smallest possible server size:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/G8mEC13pp.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Although production servers tend to be much bigger and more powerful than this, a tiny server will be more than enough for this article.</p>
<p>Finally, for the last step, put something fitting like <strong>nginx-hadnbook-demo-server</strong> as the server host and label. You can even leave them empty if you want. </p>
<p>Once you're happy with your choices, go ahead and press the <strong>Deploy Now</strong> button.</p>
<p>The deployment process may take some time to finish, but once it's done, you'll see the newly created server on your dashboard:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/server-list.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Also pay attention to the <strong>Status –</strong> it should say <strong>Running</strong> and not <strong>Preparing</strong> or <strong>Stopped</strong>. To connect to the server, you'll need a username and password.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/server-overview.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Go into the overview page for your server and there you should see the server's IP address, username, and password:</p>
<p>The generic command for logging into a server using SSH is as follows:</p>
<pre><code class="lang-shell">ssh &lt;username&gt;@&lt;ip address&gt;
</code></pre>
<p>So in the case of my server, it'll be:</p>
<pre><code class="lang-shell">ssh root@45.77.251.108

# Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
# Warning: Permanently added '45.77.251.108' (ECDSA) to the list of known hosts.

# root@45.77.251.108's password: 
# Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-65-generic x86_64)

# root@localhost:~#
</code></pre>
<p>You'll be asked if you want to continue connecting to this server or not. Answer with <code>yes</code> and then you'll be asked for the password. Copy the password from the server overview page and paste that into your terminal. </p>
<p>If you do everything correctly you should be logged into your server – you'll see the <code>root@localhost</code> line on your terminal. Here <code>localhost</code> is the server host name, and may differ in your case.</p>
<p>You can access this server directly by its IP address. Or if you own any custom domain, you can use that also. </p>
<p>Throughout the article you'll see me adding test domains to my operating system's <code>hosts</code> file. In case of a real server, you'll have to configure those servers using your DNS provider.</p>
<p>Remember that you'll be charged as long as this server is being used. Although the charge should be very small, I'm warning you anyways. You can destroy the server anytime you want by hitting the trash icon on the server overview page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-90.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you own a custom domain name, you may assign a sub-domain to this server. Now that you're inside the server, all that is left to is <a class="post-section-overview" href="#heading-how-to-install-nginx-on-a-provisioned-server-or-virtual-machine-2">install NGINX</a>.</p>
<h3 id="heading-how-to-install-nginx-on-a-provisioned-server-or-virtual-machine">How to Install NGINX on a Provisioned Server or Virtual Machine</h3>
<p>Assuming you're logged into your server or virtual machine, the first thing you should do is performing an update. Execute the following command to do so:</p>
<pre><code class="lang-shell">sudo apt update &amp;&amp; sudo apt upgrade -y
</code></pre>
<p>After the update, install NGINX by executing the following command:</p>
<pre><code class="lang-shell">sudo apt install nginx -y
</code></pre>
<p>Once the installation is done, NGINX should be automatically registered as a <code>systemd</code> service and should be running. To check, execute the following command:</p>
<pre><code class="lang-shell">sudo systemctl status nginx

# ● nginx.service - A high performance web server and a reverse proxy server
#      Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
#      Active: active (running)
</code></pre>
<p>If the status says <code>running</code>, then you're good to go. Otherwise you may start the service by executing this command:</p>
<pre><code class="lang-shell">sudo systemctl start nginx
</code></pre>
<p>Finally for a visual verification that everything is working properly, visit your server/virtual machine with your favorite browser and you should see NGINX's default welcome page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-89.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>NGINX is usually installed on the <code>/etc/nginx</code> directory and the majority of our work in the upcoming sections will be done in here.</p>
<p>Congratulations! Bow you have NGINX up and running on your server/virtual machine. Now it's time to jump head first into NGINX.</p>
<h2 id="heading-introduction-to-nginxs-configuration-files">Introduction to NGINX's Configuration Files</h2>
<p>As a web server, NGINX's job is to serve static or dynamic contents to the clients. But how that content are going to be served is usually controlled by configuration files.</p>
<p>NGINX's configuration files end with the <code>.conf</code> extension and usually live inside the <code>/etc/nginx/</code> directory. Let's begin by <code>cd</code>ing into this directory and getting a list of all the files:</p>
<pre><code class="lang-shell">cd /etc/nginx

ls -lh

# drwxr-xr-x 2 root root 4.0K Apr 21  2020 conf.d
# -rw-r--r-- 1 root root 1.1K Feb  4  2019 fastcgi.conf
# -rw-r--r-- 1 root root 1007 Feb  4  2019 fastcgi_params
# -rw-r--r-- 1 root root 2.8K Feb  4  2019 koi-utf
# -rw-r--r-- 1 root root 2.2K Feb  4  2019 koi-win
# -rw-r--r-- 1 root root 3.9K Feb  4  2019 mime.types
# drwxr-xr-x 2 root root 4.0K Apr 21  2020 modules-available
# drwxr-xr-x 2 root root 4.0K Apr 17 14:42 modules-enabled
# -rw-r--r-- 1 root root 1.5K Feb  4  2019 nginx.conf
# -rw-r--r-- 1 root root  180 Feb  4  2019 proxy_params
# -rw-r--r-- 1 root root  636 Feb  4  2019 scgi_params
# drwxr-xr-x 2 root root 4.0K Apr 17 14:42 sites-available
# drwxr-xr-x 2 root root 4.0K Apr 17 14:42 sites-enabled
# drwxr-xr-x 2 root root 4.0K Apr 17 14:42 snippets
# -rw-r--r-- 1 root root  664 Feb  4  2019 uwsgi_params
# -rw-r--r-- 1 root root 3.0K Feb  4  2019 win-utf
</code></pre>
<p>Among these files, there should be one named <strong>nginx.conf</strong>. This is the the main configuration file for NGINX. You can have a look at the content of this file using the <code>cat</code> program:</p>
<pre><code class="lang-shell">cat nginx.conf

# user www-data;
# worker_processes auto;
# pid /run/nginx.pid;
# include /etc/nginx/modules-enabled/*.conf;

# events {
#     worker_connections 768;
#     # multi_accept on;
# }

# http {

#     ##
#     # Basic Settings
#     ##

#     sendfile on;
#     tcp_nopush on;
#     tcp_nodelay on;
#     keepalive_timeout 65;
#     types_hash_max_size 2048;
#     # server_tokens off;

#     # server_names_hash_bucket_size 64;
#     # server_name_in_redirect off;

#     include /etc/nginx/mime.types;
#     default_type application/octet-stream;

#     ##
#     # SSL Settings
#     ##

#     ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
#     ssl_prefer_server_ciphers on;

#     ##
#     # Logging Settings
#     ##

#     access_log /var/log/nginx/access.log;
#     error_log /var/log/nginx/error.log;

#     ##
#     # Gzip Settings
#     ##

#     gzip on;

#     # gzip_vary on;
#     # gzip_proxied any;
#     # gzip_comp_level 6;
#     # gzip_buffers 16 8k;
#     # gzip_http_version 1.1;
#     # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

#     ##
#     # Virtual Host Configs
#     ##

#     include /etc/nginx/conf.d/*.conf;
#     include /etc/nginx/sites-enabled/*;
# }


# #mail {
# #    # See sample authentication script at:
# #    # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
# # 
# #    # auth_http localhost/auth.php;
# #    # pop3_capabilities "TOP" "USER";
# #    # imap_capabilities "IMAP4rev1" "UIDPLUS";
# # 
# #    server {
# #        listen     localhost:110;
# #        protocol   pop3;
# #        proxy      on;
# #    }
# # 
# #    server {
# #        listen     localhost:143;
# #        protocol   imap;
# #        proxy      on;
# #    }
# #}
</code></pre>
<p>Whoa! That's a lot of stuff. Trying to understand this file at its current state will be a nightmare. So let's rename the file and create a new empty one:</p>
<pre><code class="lang-shell"># renames the file
sudo mv nginx.conf nginx.conf.backup

# creates a new file
sudo touch nginx.conf
</code></pre>
<p>I <strong>highly discourage</strong> you from editing the original <code>nginx.conf</code> file unless you absolutely know what you're doing. For learning purposes, you may rename it, but <a class="post-section-overview" href="#heading-how-to-understand-the-main-configuration-file-1">later on</a>, I'll show you how you should go about configuring a server in a real life scenario.</p>
<h2 id="heading-how-to-configure-a-basic-web-server">How to Configure a Basic Web Server</h2>
<p>In this section of the book, you'll finally get your hands dirty by configuring a basic static web server from the ground up. The goal of this section is to introduce you to the syntax and fundamental concepts of NGINX configuration files.</p>
<h3 id="heading-how-to-write-your-first-configuration-file">How to Write Your First Configuration File</h3>
<p>Start by opening the newly created <code>nginx.conf</code> file using the <a target="_blank" href="https://www.nano-editor.org/">nano</a> text editor:</p>
<pre><code class="lang-shell">sudo nano /etc/nginx/nginx.conf
</code></pre>
<p>Throughout the book, I'll be using nano as my text editor. You may use something more modern if you want to, but in a real life scenario, you're most likely to work using <a target="_blank" href="https://www.nano-editor.org/">nano</a> or <a target="_blank" href="https://www.vim.org/">vim</a> on servers instead of anything else. So use this book as an opportunity to sharpen your nano skills. Also the official <a target="_blank" href="https://www.nano-editor.org/dist/latest/cheatsheet.html">cheat sheet</a> is there for you to consult whenever you need.</p>
<p>After opening the file, update its content to look like this:</p>
<pre><code class="lang-conf">events {

}

http {

    server {

        listen 80;
        server_name nginx-handbook.test;

        return 200 "Bonjour, mon ami!\n";
    }

}
</code></pre>
<p>If you have experience building REST APIs then you may guess from the <code>return 200 "Bonjour, mon ami!\n";</code> line that the server has been configured to respond with a status code of 200 and the message "Bonjour, mon ami!".</p>
<p>Don't worry if you don't understand anything more than that at the moment. I'll explain this file line by line, but first let's see this configuration in action.</p>
<h3 id="heading-how-to-validate-and-reload-configuration-files">How to Validate and Reload Configuration Files</h3>
<p>After writing a new configuration file or updating an old one, the first thing to do is check the file for any syntax mistakes. The <code>nginx</code> binary includes an option <code>-t</code> to do just that.</p>
<pre><code class="lang-shell">sudo nginx -t

# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
</code></pre>
<p>If you have any syntax errors, this command will let you know about them, including the line number. </p>
<p>Although the configuration file is fine, NGINX will not use it. The way NGINX works is it reads the configuration file once and keeps working based on that.</p>
<p>If you update the configuration file, then you'll have to instruct NGINX explicitly to reload the configuration file. There are two ways to do that.</p>
<ul>
<li>You can restart the NGINX service by executing the <code>sudo systemctl restart nginx</code> command.</li>
<li>You can dispatch a <code>reload</code> signal to NGINX by executing the <code>sudo nginx -s reload</code> command.</li>
</ul>
<p>The <code>-s</code> option is used for dispatching various signals to NGINX. The available signals are <code>stop</code>, <code>quit</code>, <code>reload</code> and <code>reopen</code>. Among the two ways I just mentioned, I prefer the second one simply because it's less typing.</p>
<p>Once you've reloaded the configuration file by executing the <code>nginx -s reload</code> command, you can see it in action by sending a simple get request to the server:</p>
<pre><code class="lang-shell">curl -i http://nginx-handbook.test

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Wed, 21 Apr 2021 10:03:33 GMT
# Content-Type: text/plain
# Content-Length: 18
# Connection: keep-alive

# Bonjour, mon ami!
</code></pre>
<p>The server is responding with a status code of 200 and the expected message. Congratulations on getting this far! Now it's time for some explanation.</p>
<h3 id="heading-how-to-understand-directives-and-contexts-in-nginx">How to Understand Directives and Contexts in NGINX</h3>
<p>The few lines of code you've written here, although seemingly simple, introduce two of the most important terminologies of NGINX configuration files. They are <strong>directives</strong> and <strong>contexts</strong>.</p>
<p>Technically, everything inside a NGINX configuration file is a <strong>directive</strong>. Directives are of two types:</p>
<ul>
<li>Simple Directives</li>
<li>Block Directives</li>
</ul>
<p>A simple directive consists of the directive name and the space delimited parameters, like <code>listen</code>, <code>return</code> and others. Simple directives are terminated by semicolons.</p>
<p>Block directives are similar to simple directives, except that instead of ending with semicolons, they end with a pair of curly braces <code>{ }</code> enclosing additional instructions. </p>
<p>A block directive capable of containing other directives inside it is called a context, that is <code>events</code>, <code>http</code> and so on. There are four core contexts in NGINX:</p>
<ul>
<li><code>events { }</code> – The <code>events</code> context is used for setting global configuration regarding how NGINX is going to handle requests on a general level. There can be only one <code>events</code> context in a valid configuration file.</li>
<li><code>http { }</code> – Evident by the name, <code>http</code> context is used for defining configuration regarding how the server is going to handle HTTP and HTTPS requests, specifically. There can be only one <code>http</code> context in a valid configuration file.</li>
<li><code>server { }</code> – The <code>server</code> context is nested inside the <code>http</code> context and used for configuring specific virtual servers within a single host. There can be multiple <code>server</code> contexts in a valid configuration file nested inside the <code>http</code> context. Each <code>server</code> context is considered a virtual host.</li>
<li><code>main</code> – The <code>main</code> context is the configuration file itself. Anything written outside of the three previously mentioned contexts is on the <code>main</code> context.</li>
</ul>
<p>You can treat contexts in NGINX like scopes in other programming languages. There is also a sense of inheritance among them. You can find an <a target="_blank" href="https://nginx.org/en/docs/dirindex.html">alphabetical index of directives</a> on the official NGINX docs.</p>
<p>I've already mentioned that there can be multiple <code>server</code> contexts within a configuration file. But when a request reaches the server, how does NGINX know which one of those contexts should handle the request?</p>
<p>The <code>listen</code> directive is one of the ways to identify the correct <code>server</code> context within a configuration. Consider the following scenario:</p>
<pre><code>http {
    server {
        listen <span class="hljs-number">80</span>;
        server_name nginx-handbook.test;

        <span class="hljs-keyword">return</span> <span class="hljs-number">200</span> <span class="hljs-string">"hello from port 80!\n"</span>;
    }


    server {
        listen <span class="hljs-number">8080</span>;
        server_name nginx-handbook.test;

        <span class="hljs-keyword">return</span> <span class="hljs-number">200</span> <span class="hljs-string">"hello from port 8080!\n"</span>;
    }
}
</code></pre><p>Now if you send a request to http://nginx-handbook.test:80 then you'll receive "hello from port 80!" as a response. And if you send a request to http://nginx-handbook.test:8080, you'll receive "hello from port 8080!" as a response:</p>
<pre><code>curl nginx-handbook.test:<span class="hljs-number">80</span>

# hello <span class="hljs-keyword">from</span> port <span class="hljs-number">80</span>!

curl nginx-handbook.test:<span class="hljs-number">8080</span>

# hello <span class="hljs-keyword">from</span> port <span class="hljs-number">8080</span>!
</code></pre><p>These two server blocks are like two people holding telephone receivers, waiting to respond when a request reaches one of their numbers. Their numbers are indicated by the <code>listen</code> directives.</p>
<p>Apart from the <code>listen</code> directive, there is also the <code>server_name</code> directive. Consider the following scenario of an imaginary library management application:</p>
<pre><code>http {
    server {
        listen <span class="hljs-number">80</span>;
        server_name library.test;

        <span class="hljs-keyword">return</span> <span class="hljs-number">200</span> <span class="hljs-string">"your local library!\n"</span>;
    }


    server {
        listen <span class="hljs-number">80</span>;
        server_name librarian.library.test;

        <span class="hljs-keyword">return</span> <span class="hljs-number">200</span> <span class="hljs-string">"welcome dear librarian!\n"</span>;
    }
}
</code></pre><p>This is a basic example of the idea of virtual hosts. You're running two separate applications under different server names in the same server.</p>
<p>If you send a request to http://library.test then you'll get "your local library!" as a response. If you send a request to http://librarian.library.test, you'll get "welcome dear librarian!" as a response.</p>
<pre><code class="lang-shell">curl http://library.test

# your local library!

curl http://librarian.library.test

# welcome dear librarian!
</code></pre>
<p>To make this demo work on your system, you'll have to update your <code>hosts</code> file to include these two domain names as well:</p>
<pre><code class="lang-hosts">192.168.20.20   library.test
192.168.20.20   librarian.library.test
</code></pre>
<p>Finally, the <code>return</code> directive is responsible for returning a valid response to the user. This directive takes two parameters: the status code and the string message to be returned.</p>
<h3 id="heading-how-to-serve-static-content-using-nginx">How to Serve Static Content Using NGINX</h3>
<p>Now that you have a good understanding of how to write a basic configuration file for NGINX, let's upgrade the configuration to serve static files instead of plain text responses.</p>
<p>In order to serve static content, you first have to store them somewhere on your server. If you list the files and directory on the root of your server using <code>ls</code>, you'll find a directory called <code>/srv</code> in there:</p>
<pre><code class="lang-shell">ls -lh /

# lrwxrwxrwx   1 root    root       7 Apr 16 02:10 bin -&gt; usr/bin
# drwxr-xr-x   3 root    root    4.0K Apr 16 02:13 boot
# drwxr-xr-x  16 root    root    3.8K Apr 21 09:23 dev
# drwxr-xr-x  92 root    root    4.0K Apr 21 09:24 etc
# drwxr-xr-x   4 root    root    4.0K Apr 21 08:04 home
# lrwxrwxrwx   1 root    root       7 Apr 16 02:10 lib -&gt; usr/lib
# lrwxrwxrwx   1 root    root       9 Apr 16 02:10 lib32 -&gt; usr/lib32
# lrwxrwxrwx   1 root    root       9 Apr 16 02:10 lib64 -&gt; usr/lib64
# lrwxrwxrwx   1 root    root      10 Apr 16 02:10 libx32 -&gt; usr/libx32
# drwx------   2 root    root     16K Apr 16 02:15 lost+found
# drwxr-xr-x   2 root    root    4.0K Apr 16 02:10 media
# drwxr-xr-x   2 root    root    4.0K Apr 16 02:10 mnt
# drwxr-xr-x   2 root    root    4.0K Apr 16 02:10 opt
# dr-xr-xr-x 152 root    root       0 Apr 21 09:23 proc
# drwx------   5 root    root    4.0K Apr 21 09:59 root
# drwxr-xr-x  26 root    root     820 Apr 21 09:47 run
# lrwxrwxrwx   1 root    root       8 Apr 16 02:10 sbin -&gt; usr/sbin
# drwxr-xr-x   6 root    root    4.0K Apr 16 02:14 snap
# drwxr-xr-x   2 root    root    4.0K Apr 16 02:10 srv
# dr-xr-xr-x  13 root    root       0 Apr 21 09:23 sys
# drwxrwxrwt  11 root    root    4.0K Apr 21 09:24 tmp
# drwxr-xr-x  15 root    root    4.0K Apr 16 02:12 usr
# drwxr-xr-x   1 vagrant vagrant   38 Apr 21 09:23 vagrant
# drwxr-xr-x  14 root    root    4.0K Apr 21 08:34 var
</code></pre>
<p>This <code>/srv</code> directory is meant to contain site-specific data which is served by this system. Now <code>cd</code> into this directory and clone the code repository that comes with this book:</p>
<pre><code>cd /srv

sudo git clone https:<span class="hljs-comment">//github.com/fhsinchy/nginx-handbook-projects.git</span>
</code></pre><p>Inside the <code>nginx-handbook-projects</code> directory there should a directory called <code>static-demo</code> containing four files in total:</p>
<pre><code class="lang-shell">ls -lh /srv/nginx-handbook-projects/static-demo

# -rw-r--r-- 1 root root 960 Apr 21 11:27 about.html
# -rw-r--r-- 1 root root 960 Apr 21 11:27 index.html
# -rw-r--r-- 1 root root 46K Apr 21 11:27 mini.min.css
# -rw-r--r-- 1 root root 19K Apr 21 11:27 the-nginx-handbook.jpg
</code></pre>
<p>Now that you have the static content to be served, update your configuration as follows:</p>
<pre><code class="lang-conf">events {

}

http {

    server {

        listen 80;
        server_name nginx-handbook.test;

        root /srv/nginx-handbook-projects/static-demo;
    }

}
</code></pre>
<p>The code is almost the same, except the <code>return</code> directive has now been replaced by a <code>root</code> directive. This directive is used for declaring the root directory for a site. </p>
<p>By writing <code>root /srv/nginx-handbook-projects/static-demo</code> you're telling NGINX to look for files to serve inside the <code>/srv/nginx-handbook-projects/static-demo</code> directory if any request comes to this server. Since NGINX is a web server, it is smart enough to serve the <code>index.html</code> file by default.</p>
<p>Let's see if this works or not. Test and reload the updated configuration file and visit the server. You should be greeted with a somewhat broken HTML site:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-91.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Although NGINX has served the index.html file correctly, judging by the look of the three navigation links, it seems like the CSS code is not working.</p>
<p>You may think that there is something wrong in the CSS file. But in reality, the problem is in the configuration file.</p>
<h3 id="heading-static-file-type-handling-in-nginx">Static File Type Handling in NGINX</h3>
<p>To debug the issue you're facing right now, send a request for the CSS file to the server:</p>
<pre><code class="lang-shell">curl -I http://nginx-handbook/mini.min.css

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Wed, 21 Apr 2021 12:17:16 GMT
# Content-Type: text/plain
# Content-Length: 46887
# Last-Modified: Wed, 21 Apr 2021 11:27:06 GMT
# Connection: keep-alive
# ETag: "60800c0a-b727"
# Accept-Ranges: bytes
</code></pre>
<p>Pay attention to the <strong>Content-Type</strong> and see how it says <strong>text/plain</strong> and not <strong>text/css</strong>. This means that NGINX is serving this file as plain text instead of as a stylesheet.</p>
<p>Although NGINX is smart enough to find the <code>index.html</code> file by default, it's pretty dumb when it comes to interpreting file types. To solve this problem update your configuration once again:</p>
<pre><code class="lang-conf">events {

}

http {

    types {
        text/html html;
        text/css css;
    }

    server {

        listen 80;
        server_name nginx-handbook.test;

        root /srv/nginx-handbook-projects/static-demo;
    }
}
</code></pre>
<p>The only change we've made to the code is a new <code>types</code> context nested inside the <code>http</code> block. As you may have already guessed from the name, this context is used for configuring file types.</p>
<p>By writing <code>text/html html</code> in this context you're telling NGINX to parse any file as <code>text/html</code> that ends with the <code>html</code> extension.</p>
<p>You may think that configuring the CSS file type should suffice as the HTML is being parsed just fine – but no. </p>
<p>If you introduce a <code>types</code> context in the configuration, NGINX becomes even dumber and only parses the files configured by you. So if you only define the <code>text/css css</code> in this context then NGINX will start parsing the HTML file as plain text.</p>
<p>Validate and reload the newly updated config file and visit the server once again. Send a request for the CSS file once again, and this time the file should be parsed as a <strong>text/css</strong> file:</p>
<pre><code class="lang-shell">curl -I http://nginx-handbook.test/mini.min.css

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Wed, 21 Apr 2021 12:29:35 GMT
# Content-Type: text/css
# Content-Length: 46887
# Last-Modified: Wed, 21 Apr 2021 11:27:06 GMT
# Connection: keep-alive
# ETag: "60800c0a-b727"
# Accept-Ranges: bytes
</code></pre>
<p>Visit the server for a visual verification, and the site should look better this time:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-92.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you've updated and reloaded the configuration file correctly and you're still seeing the old site, perform a hard refresh.</p>
<h3 id="heading-how-to-include-partial-config-files">How to Include Partial Config Files</h3>
<p>Mapping file types within the <code>types</code> context may work for small projects, but for bigger projects it can be cumbersome and error-prone.</p>
<p>NGINX provides a solution for this problem. If you list the files inside the <code>/etc/nginx</code> directory once again, you'll see a file named <code>mime.types</code>.</p>
<pre><code class="lang-shell">ls -lh /etc/nginx

# drwxr-xr-x 2 root root 4.0K Apr 21  2020 conf.d
# -rw-r--r-- 1 root root 1.1K Feb  4  2019 fastcgi.conf
# -rw-r--r-- 1 root root 1007 Feb  4  2019 fastcgi_params
# -rw-r--r-- 1 root root 2.8K Feb  4  2019 koi-utf
# -rw-r--r-- 1 root root 2.2K Feb  4  2019 koi-win
# -rw-r--r-- 1 root root 3.9K Feb  4  2019 mime.types
# drwxr-xr-x 2 root root 4.0K Apr 21  2020 modules-available
# drwxr-xr-x 2 root root 4.0K Apr 17 14:42 modules-enabled
# -rw-r--r-- 1 root root 1.5K Feb  4  2019 nginx.conf
# -rw-r--r-- 1 root root  180 Feb  4  2019 proxy_params
# -rw-r--r-- 1 root root  636 Feb  4  2019 scgi_params
# drwxr-xr-x 2 root root 4.0K Apr 17 14:42 sites-available
# drwxr-xr-x 2 root root 4.0K Apr 17 14:42 sites-enabled
# drwxr-xr-x 2 root root 4.0K Apr 17 14:42 snippets
# -rw-r--r-- 1 root root  664 Feb  4  2019 uwsgi_params
# -rw-r--r-- 1 root root 3.0K Feb  4  2019 win-utf
</code></pre>
<p>Let's have a look at the content of this file:</p>
<pre><code class="lang-shell">cat /etc/mime.types

# types {
#     text/html                             html htm shtml;
#     text/css                              css;
#     text/xml                              xml;
#     image/gif                             gif;
#     image/jpeg                            jpeg jpg;
#     application/javascript                js;
#     application/atom+xml                  atom;
#     application/rss+xml                   rss;

#     text/mathml                           mml;
#     text/plain                            txt;
#     text/vnd.sun.j2me.app-descriptor      jad;
#     text/vnd.wap.wml                      wml;
#     text/x-component                      htc;

#     image/png                             png;
#     image/tiff                            tif tiff;
#     image/vnd.wap.wbmp                    wbmp;
#     image/x-icon                          ico;
#     image/x-jng                           jng;
#     image/x-ms-bmp                        bmp;
#     image/svg+xml                         svg svgz;
#     image/webp                            webp;

#     application/font-woff                 woff;
#     application/java-archive              jar war ear;
#     application/json                      json;
#     application/mac-binhex40              hqx;
#     application/msword                    doc;
#     application/pdf                       pdf;
#     application/postscript                ps eps ai;
#     application/rtf                       rtf;
#     application/vnd.apple.mpegurl         m3u8;
#     application/vnd.ms-excel              xls;
#     application/vnd.ms-fontobject         eot;
#     application/vnd.ms-powerpoint         ppt;
#     application/vnd.wap.wmlc              wmlc;
#     application/vnd.google-earth.kml+xml  kml;
#     application/vnd.google-earth.kmz      kmz;
#     application/x-7z-compressed           7z;
#     application/x-cocoa                   cco;
#     application/x-java-archive-diff       jardiff;
#     application/x-java-jnlp-file          jnlp;
#     application/x-makeself                run;
#     application/x-perl                    pl pm;
#     application/x-pilot                   prc pdb;
#     application/x-rar-compressed          rar;
#     application/x-redhat-package-manager  rpm;
#     application/x-sea                     sea;
#     application/x-shockwave-flash         swf;
#     application/x-stuffit                 sit;
#     application/x-tcl                     tcl tk;
#     application/x-x509-ca-cert            der pem crt;
#     application/x-xpinstall               xpi;
#     application/xhtml+xml                 xhtml;
#     application/xspf+xml                  xspf;
#     application/zip                       zip;

#     application/octet-stream              bin exe dll;
#     application/octet-stream              deb;
#     application/octet-stream              dmg;
#     application/octet-stream              iso img;
#     application/octet-stream              msi msp msm;

#     application/vnd.openxmlformats-officedocument.wordprocessingml.document    docx;
#     application/vnd.openxmlformats-officedocument.spreadsheetml.sheet          xlsx;
#     application/vnd.openxmlformats-officedocument.presentationml.presentation  pptx;

#     audio/midi                            mid midi kar;
#     audio/mpeg                            mp3;
#     audio/ogg                             ogg;
#     audio/x-m4a                           m4a;
#     audio/x-realaudio                     ra;

#     video/3gpp                            3gpp 3gp;
#     video/mp2t                            ts;
#     video/mp4                             mp4;
#     video/mpeg                            mpeg mpg;
#     video/quicktime                       mov;
#     video/webm                            webm;
#     video/x-flv                           flv;
#     video/x-m4v                           m4v;
#     video/x-mng                           mng;
#     video/x-ms-asf                        asx asf;
#     video/x-ms-wmv                        wmv;
#     video/x-msvideo                       avi;
# }
</code></pre>
<p>The file contains a long list of file types and their extensions. To use this file inside your configuration file, update your configuration to look as follows:</p>
<pre><code class="lang-conf">events {

}

http {

    include /etc/nginx/mime.types;

    server {

        listen 80;
        server_name nginx-handbook.test;

        root /srv/nginx-handbook-projects/static-demo;
    }

}
</code></pre>
<p>The old <code>types</code> context has now been replaced with a new <code>include</code> directive. Like the name suggests, this directive allows you to include content from other configuration files.</p>
<p>Validate and reload the configuration file and send a request for the <code>mini.min.css</code> file once again:</p>
<pre><code class="lang-shell">curl -I http://nginx-handbook.test/mini.min.css

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Wed, 21 Apr 2021 12:29:35 GMT
# Content-Type: text/css
# Content-Length: 46887
# Last-Modified: Wed, 21 Apr 2021 11:27:06 GMT
# Connection: keep-alive
# ETag: "60800c0a-b727"
# Accept-Ranges: bytes
</code></pre>
<p>In the section below on how to understand the main configuration file, I'll demonstrate how <code>include</code> can be used to modularize your virtual server configurations.</p>
<h2 id="heading-dynamic-routing-in-nginx">Dynamic Routing in NGINX</h2>
<p>The configuration you wrote in the previous section was a very simple static content server configuration. All it did was match a file from the site root corresponding to the URI the client visits and respond back.</p>
<p>So if the client requests files existing on the root such as <code>index.html</code>, <code>about.html</code> or <code>mini.min.css</code> NGINX will return the file. But if you visit a route such as http://nginx-handbook.test/nothing, it'll respond with the default 404 page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-93.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In this section of the book, you'll learn about the <code>location</code> context, variables, redirects, rewrites and the <code>try_files</code> directive. There will be no new projects in this section but the concepts you learn here will be necessary in the upcoming sections.</p>
<p>Also the configuration will change very frequently in this section, so do not forget to validate and reload the configuration file after every update.</p>
<h3 id="heading-location-matches">Location Matches</h3>
<p>The first concept we'll discuss in this section is the <code>location</code> context. Update the configuration as follows:</p>
<pre><code class="lang-conf">events {

}

http {

    server {

        listen 80;
        server_name nginx-handbook.test;

        location /agatha {
            return 200 "Miss Marple.\nHercule Poirot.\n";
        }
    }
}
</code></pre>
<p>We've replaced the <code>root</code> directive with a new <code>location</code> context. This context is usually nested inside <code>server</code> blocks. There can be multiple <code>location</code> contexts within a <code>server</code> context.</p>
<p>If you send a request to http://nginx-handbook.test/agatha, you'll get a 200 response code and list of characters created by <a target="_blank" href="https://en.wikipedia.org/wiki/Agatha_Christie">Agatha Christie</a>.</p>
<pre><code class="lang-shell">curl -i http://nginx-handbook.test/agatha

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Wed, 21 Apr 2021 15:59:07 GMT
# Content-Type: text/plain
# Content-Length: 29
# Connection: keep-alive

# Miss Marple.
# Hercule Poirot.
</code></pre>
<p>Now if you send a request to http://nginx-handbook.test/agatha-christie, you'll get the same response:</p>
<pre><code class="lang-shell">curl -i http://nginx-handbook.test/agatha-christie

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Wed, 21 Apr 2021 15:59:07 GMT
# Content-Type: text/plain
# Content-Length: 29
# Connection: keep-alive

# Miss Marple.
# Hercule Poirot.
</code></pre>
<p>This happens because, by writing <code>location /agatha</code>, you're telling NGINX to match any URI starting with "agatha".  This kind of match is called a <strong>prefix match</strong>.</p>
<p>To perform an <strong>exact match</strong>, you'll have to update the code as follows:</p>
<pre><code class="lang-conf">events {

}

http {

    server {

        listen 80;
        server_name nginx-handbook.test;

        location = /agatha {
            return 200 "Miss Marple.\nHercule Poirot.\n";
        }
    }

}
</code></pre>
<p>Adding an <code>=</code> sign before the location URI will instruct NGINX to respond only if the URL matches exactly. Now if you send a request to anything but <code>/agatha</code>, you'll get a 404 response.</p>
<pre><code class="lang-shell">curl -I http://nginx-handbook.test/agatha-christie

# HTTP/1.1 404 Not Found
# Server: nginx/1.18.0 (Ubuntu)
# Date: Wed, 21 Apr 2021 16:14:29 GMT
# Content-Type: text/html
# Content-Length: 162
# Connection: keep-alive

curl -I http://nginx-handbook.test/agatha

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Wed, 21 Apr 2021 16:15:04 GMT
# Content-Type: text/plain
# Content-Length: 29
# Connection: keep-alive
</code></pre>
<p>Another kind of match in NGINX is the <strong>regex match</strong>. Using this match you can check location URLs against complex regular expressions.</p>
<pre><code class="lang-conf">events {

}

http {

    server {

        listen 80;
        server_name nginx-handbook.test;

        location ~ /agatha[0-9] {
            return 200 "Miss Marple.\nHercule Poirot.\n";
        }
    }

}
</code></pre>
<p>By replacing the previously used <code>=</code> sign with a <code>~</code> sign, you're telling NGINX to perform a regular expression match. Setting the location to <code>~ /agatha[0-9]</code> means NIGINX will only respond if there is a number after the word "agatha":</p>
<pre><code class="lang-shell">curl -I http://nginx-handbook.test/agatha

# HTTP/1.1 404 Not Found
# Server: nginx/1.18.0 (Ubuntu)
# Date: Wed, 21 Apr 2021 16:14:29 GMT
# Content-Type: text/html
# Content-Length: 162
# Connection: keep-alive

curl -I http://nginx-handbook.test/agatha8

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Wed, 21 Apr 2021 16:15:04 GMT
# Content-Type: text/plain
# Content-Length: 29
# Connection: keep-alive
</code></pre>
<p>A regex match is by default case sensitive, which means that if you capitalize any of the letters, the location won't work:</p>
<pre><code class="lang-shell">curl -I http://nginx-handbook.test/Agatha8

# HTTP/1.1 404 Not Found
# Server: nginx/1.18.0 (Ubuntu)
# Date: Wed, 21 Apr 2021 16:14:29 GMT
# Content-Type: text/html
# Content-Length: 162
# Connection: keep-alive
</code></pre>
<p>To turn this into case insensitive, you'll have to add a <code>*</code> after the <code>~</code> sign.</p>
<pre><code class="lang-conf">events {

}

http {

    server {

        listen 80;
        server_name nginx-handbook.test;

        location ~* /agatha[0-9] {
            return 200 "Miss Marple.\nHercule Poirot.\n";
        }
    }

}
</code></pre>
<p>That will tell NGINX to let go of type sensitivity and match the location anyways.</p>
<pre><code class="lang-shell">curl -I http://nginx-handbook.test/agatha8

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Wed, 21 Apr 2021 16:15:04 GMT
# Content-Type: text/plain
# Content-Length: 29
# Connection: keep-alive

curl -I http://nginx-handbook.test/Agatha8

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Wed, 21 Apr 2021 16:15:04 GMT
# Content-Type: text/plain
# Content-Length: 29
# Connection: keep-alive
</code></pre>
<p>NGINX assigns priority values to these matches, and a regex match has more priority than a prefix match.</p>
<pre><code class="lang-conf">events {

}

http {

    server {

        listen 80;
        server_name nginx-handbook.test;

        location /Agatha8 {
            return 200 "prefix matched.\n";
        }

        location ~* /agatha[0-9] {
            return 200 "regex matched.\n";
        }
    }

}
</code></pre>
<p>Now if you send a request to http://nginx-handbook.test/Agatha8, you'll get the following response:</p>
<pre><code class="lang-shell">curl -i http://nginx-handbook.test/Agatha8

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Thu, 22 Apr 2021 08:08:18 GMT
# Content-Type: text/plain
# Content-Length: 15
# Connection: keep-alive

# regex matched.
</code></pre>
<p>But this priority can be changed a little. The final type of match in NGINX is a <strong>preferential prefix match</strong>. To turn a prefix match into a preferential one, you need to include the <code>^~</code> modifier before the location URI:</p>
<pre><code class="lang-conf">events {

}

http {

    server {

        listen 80;
        server_name nginx-handbook.test;

        location ^~ /Agatha8 {
            return 200 "prefix matched.\n";
        }

        location ~* /agatha[0-9] {
            return 200 "regex matched.\n";
        }
    }

}
</code></pre>
<p>Now if you send a request to http://nginx-handbook.test/Agatha8, you'll get the following response:</p>
<pre><code class="lang-shell">curl -i http://nginx-handbook.test/Agatha8

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Thu, 22 Apr 2021 08:13:24 GMT
# Content-Type: text/plain
# Content-Length: 16
# Connection: keep-alive

# prefix matched.
</code></pre>
<p>This time, the prefix match wins. So the list of all the matches in descending order of priority is as follows:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Match</td><td>Modifier</td></tr>
</thead>
<tbody>
<tr>
<td>Exact</td><td><code>=</code></td></tr>
<tr>
<td>Preferential Prefix</td><td><code>^~</code></td></tr>
<tr>
<td>REGEX</td><td><code>~</code> or <code>~*</code></td></tr>
<tr>
<td>Prefix</td><td><code>None</code></td></tr>
</tbody>
</table>
</div><h3 id="heading-variables-in-nginx">Variables in NGINX</h3>
<p>Variables in NGINX are similar to variables in other programming languages. The <code>set</code> directive can be used to declare new variables anywhere within the configuration file:</p>
<pre><code class="lang-conf">set $&lt;variable_name&gt; &lt;variable_value&gt;;

# set name "Farhan"
# set age 25
# set is_working true
</code></pre>
<p>Variables can be of three types</p>
<ul>
<li>String</li>
<li>Integer</li>
<li>Boolean</li>
</ul>
<p>Apart from the variables you declare, there are embedded variables within NGINX modules. An <a target="_blank" href="https://nginx.org/en/docs/varindex.html">alphabetical index of variables</a> is available in the official documentation.</p>
<p>To see some of the variables in action, update the configuration as follows:</p>
<pre><code class="lang-conf">events {

}

http {

    server {

        listen 80;
        server_name nginx-handbook.test;

        return 200 "Host - $host\nURI - $uri\nArgs - $args\n";
    }

}
</code></pre>
<p>Now upon sending a request to the server, you should get a response as follows:</p>
<pre><code class="lang-shell"># curl http://nginx-handbook.test/user?name=Farhan

# Host - nginx-handbook.test
# URI - /user
# Args - name=Farhan
</code></pre>
<p>As you can see, the <code>$host</code> and <code>$uri</code> variables hold the root address and the requested URI relative to the root, respectively. The <code>$args</code> variable, as you can see, contains all the query strings. </p>
<p>Instead of printing the literal string form of the query strings, you can access the individual values using the <code>$arg</code> variable.</p>
<pre><code class="lang-conf">events {

}

http {

    server {

        listen 80;
        server_name nginx-handbook.test;

        set $name $arg_name; # $arg_&lt;query string name&gt;

        return 200 "Name - $name\n";
    }

}
</code></pre>
<p>Now the response from the server should look like as follows:</p>
<pre><code class="lang-shell">curl http://nginx-handbook.test?name=Farhan

# Name - Farhan
</code></pre>
<p>The variables I demonstrated here are embedded in the <a target="_blank" href="https://nginx.org/en/docs/http/ngx_http_core_module.html">ngx_http_core_module</a>. For a variable to be accessible in the configuration, NGINX has to be built with the module embedding the variable. Building NGINX from source and usage of dynamic modules is slightly out of scope for this article. But I'll surely write about that in my blog.</p>
<h3 id="heading-redirects-and-rewrites">Redirects and Rewrites</h3>
<p>A redirect in NGINX is same as redirects in any other platform. To demonstrate how redirects work, update your configuration to look like this:</p>
<pre><code class="lang-conf">events {

}

http {

    include /etc/nginx/mime.types;

    server {

        listen 80;
        server_name nginx-handbook.test;

        root /srv/nginx-handbook-projects/static-demo;

        location = /index_page {
                return 307 /index.html;
        }

        location = /about_page {
                return 307 /about.html;
        }
    }
}
</code></pre>
<p>Now if you send a request to http://nginx-handbook.test/about_page, you'll be redirected to http://nginx-handbook.test/about.html:</p>
<pre><code class="lang-shell">curl -I http://nginx-handbook.test/about_page

# HTTP/1.1 307 Temporary Redirect
# Server: nginx/1.18.0 (Ubuntu)
# Date: Thu, 22 Apr 2021 18:02:04 GMT
# Content-Type: text/html
# Content-Length: 180
# Location: http://nginx-handbook.test/about.html
# Connection: keep-alive
</code></pre>
<p>As you can see, the server responded with a status code of 307 and the location indicates http://nginx-handbook.test/about.html. If you visit http://nginx-handbook.test/about_page from a browser, you'll see that the URL will automatically change to http://nginx-handbook.test/about.html.</p>
<p>A <code>rewrite</code> directive, however, works a little differently. It changes the URI internally, without letting the user know. To see it in action, update your configuration as follows:</p>
<pre><code class="lang-conf">events {

}

http {

    include /etc/nginx/mime.types;

    server {

        listen 80;
        server_name nginx-handbook.test;

        root /srv/nginx-handbook-projects/static-demo;

        rewrite /index_page /index.html;

        rewrite /about_page /about.html;
    }
}
</code></pre>
<p>Now if you send a request to http://nginx-handbook/about_page URI, you'll get a 200 response code and the HTML code for about.html file in response:</p>
<pre><code class="lang-shell">curl -i http://nginx-handbook.test/about_page

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Thu, 22 Apr 2021 18:09:31 GMT
# Content-Type: text/html
# Content-Length: 960
# Last-Modified: Wed, 21 Apr 2021 11:27:06 GMT
# Connection: keep-alive
# ETag: "60800c0a-3c0"
# Accept-Ranges: bytes

# &lt;!DOCTYPE html&gt;
# &lt;html lang="en"&gt;
# &lt;head&gt;
#     &lt;meta charset="UTF-8"&gt;
#     &lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt;
#     &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
#     &lt;title&gt;NGINX Handbook Static Demo&lt;/title&gt;
#     &lt;link rel="stylesheet" href="mini.min.css"&gt;
#     &lt;style&gt;
#         .container {
#             max-width: 1024px;
#             margin-left: auto;
#             margin-right: auto;
#         }
# 
#         h1 {
#             text-align: center;
#         }
#     &lt;/style&gt;
# &lt;/head&gt;
# &lt;body class="container"&gt;
#     &lt;header&gt;
#         &lt;a class="button" href="index.html"&gt;Index&lt;/a&gt;
#         &lt;a class="button" href="about.html"&gt;About&lt;/a&gt;
#         &lt;a class="button" href="nothing"&gt;Nothing&lt;/a&gt;
#     &lt;/header&gt;
#     &lt;div class="card fluid"&gt;
#         &lt;img src="./the-nginx-handbook.jpg" alt="The NGINX Handbook Cover Image"&gt;
#     &lt;/div&gt;
#     &lt;div class="card fluid"&gt;
#         &lt;h1&gt;this is the &lt;strong&gt;about.html&lt;/strong&gt; file&lt;/h1&gt;
#     &lt;/div&gt;
# &lt;/body&gt;
# &lt;/html&gt;
</code></pre>
<p>And if you visit the URI using a browser, you'll see the about.html page while the URL remains unchanged:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/rewrite.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Apart from the way the URI change is handled, there is another difference between a redirect and rewrite. When a rewrite happens, the <code>server</code> context gets re-evaluated by NGINX. So, a rewrite is a more expensive operation than a redirect.</p>
<h3 id="heading-how-to-try-for-multiple-files">How to Try for Multiple Files</h3>
<p>The final concept I'll be showing in this section is the <code>try_files</code> directive. Instead of responding with a single file, the <code>try_files</code> directive lets you check for the existence of multiple files.</p>
<pre><code class="lang-conf">events {

}

http {

    include /etc/nginx/mime.types;

    server {

        listen 80;
        server_name nginx-handbook.test;

        root /srv/nginx-handbook-projects/static-demo;

        try_files /the-nginx-handbook.jpg /not_found;

        location /not_found {
                return 404 "sadly, you've hit a brick wall buddy!\n";
        }
    }
}
</code></pre>
<p>As you can see, a new <code>try_files</code> directive has been added. By writing <code>try_files /the-nginx-handbook.jpg /not_found;</code> you're instructing NGINX to look for a file named the-nginx-handbook.jpg on the root whenever a request is received. If it doesn't exist, go to the <code>/not_found</code> location. </p>
<p>So now if you visit the server, you'll see the image:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-94.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>But if you update the configuration to try for a non-existent file such as blackhole.jpg, you'll get a 404 response with the message "sadly, you've hit a brick wall buddy!".</p>
<p>Now the problem with writing a <code>try_files</code> directive this way is that no matter what URL you visit, as long as a request is received by the server and the the-nginx-handbook.jpg file is found on the disk, NGINX will send that back.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/try-files.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And that's why <code>try_files</code> is often used with the <code>$uri</code> NGINX variable. </p>
<pre><code class="lang-conf">events {

}

http {

    include /etc/nginx/mime.types;

    server {

        listen 80;
        server_name nginx-handbook.test;

        root /srv/nginx-handbook-projects/static-demo;

        try_files $uri /not_found;

        location /not_found {
                return 404 "sadly, you've hit a brick wall buddy!\n";
        }
    }
}
</code></pre>
<p>By writing <code>try_files $uri /not_found;</code> you're instructing NGINX to try for the URI requested by the client first. If it doesn't find that one, then try the next one.</p>
<p>So now if you visit http://nginx-handbook.test/index.html you should get the old index.html page. The same goes for the about.html page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-95.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>But if you request a file that doesn't exist, you'll get the response from the <code>/not_found</code> location:</p>
<pre><code class="lang-shell">curl -i http://nginx-handbook.test/nothing

# HTTP/1.1 404 Not Found
# Server: nginx/1.18.0 (Ubuntu)
# Date: Thu, 22 Apr 2021 20:01:57 GMT
# Content-Type: text/plain
# Content-Length: 38
# Connection: keep-alive

# sadly, you've hit a brick wall buddy!
</code></pre>
<p>One thing that you may have already noticed is that if you visit the server root http://nginx-handbook.test, you get the 404 response.</p>
<p>This is because when you're hitting the server root, the <code>$uri</code> variable doesn't correspond to any existing file so NGINX serves you the fallback location. If you want to fix this issue, update your configuration as follows:</p>
<pre><code class="lang-conf">events {

}

http {

    include /etc/nginx/mime.types;

    server {

        listen 80;
        server_name nginx-handbook.test;

        root /srv/nginx-handbook-projects/static-demo;

        try_files $uri $uri/ /not_found;

        location /not_found {
                return 404 "sadly, you've hit a brick wall buddy!\n";
        }
    }
}
</code></pre>
<p>By writing <code>try_files $uri $uri/ /not_found;</code> you're instructing NGINX to try for the requested URI first. If that doesn't work then try for the requested URI as a directory, and whenever NGINX ends up into a directory it automatically starts looking for an index.html file.</p>
<p>Now if you visit the server, you should get the index.html file just right:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-95.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The <code>try_files</code> is the kind of directive that can be used in a number of variations. In the upcoming sections, you'll encounter a few other variations but I would suggest that you do some research on the internet regarding the different usage of this directive by yourself.</p>
<h2 id="heading-logging-in-nginx">Logging in NGINX</h2>
<p>By default, NGINX's log files are located inside <code>/var/log/nginx</code>. If you list the content of this directory, you may see something as follows:</p>
<pre><code class="lang-shell">ls -lh /var/log/nginx/

# -rw-r----- 1 www-data adm     0 Apr 25 07:34 access.log
# -rw-r----- 1 www-data adm     0 Apr 25 07:34 error.log
</code></pre>
<p>Let's begin by emptying the two files.</p>
<pre><code class="lang-shell"># delete the old files
sudo rm /var/log/nginx/access.log /var/log/nginx/error.log

# create new files
sudo touch /var/log/nginx/access.log /var/log/nginx/error.log

# reopen the log files
sudo nginx -s reopen
</code></pre>
<p>If you do not dispatch a <code>reopen</code> signal to NGINX, it'll keep writing logs to the previously open streams and the new files will remain empty.</p>
<p>Now to make an entry in the access log, send a request to the server.</p>
<pre><code>curl -I http:<span class="hljs-comment">//nginx-handbook.test</span>

# HTTP/<span class="hljs-number">1.1</span> <span class="hljs-number">200</span> OK
# Server: nginx/<span class="hljs-number">1.18</span><span class="hljs-number">.0</span> (Ubuntu)
# <span class="hljs-built_in">Date</span>: Sun, <span class="hljs-number">25</span> Apr <span class="hljs-number">2021</span> <span class="hljs-number">08</span>:<span class="hljs-number">35</span>:<span class="hljs-number">59</span> GMT
# Content-Type: text/html
# Content-Length: <span class="hljs-number">960</span>
# Last-Modified: Sun, <span class="hljs-number">25</span> Apr <span class="hljs-number">2021</span> <span class="hljs-number">08</span>:<span class="hljs-number">35</span>:<span class="hljs-number">33</span> GMT
# Connection: keep-alive
# ETag: <span class="hljs-string">"608529d5-3c0"</span>
# Accept-Ranges: bytes

sudo cat /<span class="hljs-keyword">var</span>/log/nginx/access.log 

# <span class="hljs-number">192.168</span><span class="hljs-number">.20</span><span class="hljs-number">.20</span> - - [<span class="hljs-number">25</span>/Apr/<span class="hljs-number">2021</span>:<span class="hljs-number">08</span>:<span class="hljs-number">35</span>:<span class="hljs-number">59</span> +<span class="hljs-number">0000</span>] <span class="hljs-string">"HEAD / HTTP/1.1"</span> <span class="hljs-number">200</span> <span class="hljs-number">0</span> <span class="hljs-string">"-"</span> <span class="hljs-string">"curl/7.68.0"</span>
</code></pre><p>As you can see, a new entry has been added to the access.log file. Any request to the server will be logged to this file by default. But we can change this behavior using the <code>access_log</code> directive.</p>
<pre><code class="lang-conf">events {

}

http {

    include /etc/nginx/mime.types;

    server {

        listen 80;
        server_name nginx-handbook.test;

        location / {
            return 200 "this will be logged to the default file.\n";
        }

        location = /admin {
            access_log /var/logs/nginx/admin.log;

            return 200 "this will be logged in a separate file.\n";
        }

        location = /no_logging {
            access_log off;

            return 200 "this will not be logged.\n";
        }
    }
}
</code></pre>
<p>The first <code>access_log</code> directive inside the /admin location block instructs NGINX to write any access log of this URI to the <code>/var/logs/nginx/admin.log</code> file. The second one inside the /no_logging location turns off access logs for this location completely.</p>
<p>Validate and reload the configuration. Now if you send requests to these locations and inspect the log files, you should see something like this:</p>
<pre><code class="lang-shell">curl http://nginx-handbook.test/no_logging
# this will not be logged

sudo cat /var/log/nginx/access.log
# empty

curl http://nginx-handbook.test/admin
# this will be logged in a separate file.

sudo cat /var/log/nginx/access.log
# empty

sudo cat /var/log/nginx/admin.log 
# 192.168.20.20 - - [25/Apr/2021:11:13:53 +0000] "GET /admin HTTP/1.1" 200 40 "-" "curl/7.68.0"

curl  http://nginx-handbook.test/
# this will be logged to the default file.

sudo cat /var/log/nginx/access.log 
# 192.168.20.20 - - [25/Apr/2021:11:15:14 +0000] "GET / HTTP/1.1" 200 41 "-" "curl/7.68.0"
</code></pre>
<p>The error.log file, on the other hand, holds the failure logs. To make an entry to the error.log, you'll have to make NGINX crash. To do so, update your configuration as follows:</p>
<pre><code class="lang-conf">events {

}

http {

    include /etc/nginx/mime.types;

    server {

        listen 80;
        server_name nginx-handbook.test;

        return 200 "..." "...";
    }

}
</code></pre>
<p>As you know, the <code>return</code> directive takes only two parameters – but we've given three here. Now try reloading the configuration and you'll be presented with an error message:</p>
<pre><code class="lang-shell">sudo nginx -s reload

# nginx: [emerg] invalid number of arguments in "return" directive in /etc/nginx/nginx.conf:14
</code></pre>
<p>Check the content of the error log and the message should be present there as well:</p>
<pre><code class="lang-shell">sudo cat /var/log/nginx/error.log 

# 2021/04/25 08:35:45 [notice] 4169#4169: signal process started
# 2021/04/25 10:03:18 [emerg] 8434#8434: invalid number of arguments in "return" directive in /etc/nginx/nginx.conf:14
</code></pre>
<p>Error messages have levels. A <code>notice</code> entry in the error log is harmless, but an <code>emerg</code> or emergency entry has to be addressed right away. </p>
<p>There are eight levels of error messages:</p>
<ul>
<li><code>debug</code> – Useful debugging information to help determine where the problem lies.</li>
<li><code>info</code> – Informational messages that aren't necessary to read but may be good to know.</li>
<li><code>notice</code> – Something normal happened that is worth noting.</li>
<li><code>warn</code> – Something unexpected happened, however is not a cause for concern.</li>
<li><code>error</code> – Something was unsuccessful.</li>
<li><code>crit</code> – There are problems that need to be critically addressed.</li>
<li><code>alert</code> – Prompt action is required.</li>
<li><code>emerg</code> – The system is in an unusable state and requires immediate attention.</li>
</ul>
<p>By default, NGINX records all level of messages. You can override this behavior using the <code>error_log</code> directive. If you want to set the minimum level of a message to be <code>warn</code>, then update your configuration file as follows:</p>
<pre><code class="lang-conf">events {

}

http {

    include /etc/nginx/mime.types;

    server {

        listen 80;
        server_name nginx-handbook.test;

        error_log /var/log/error.log warn;

        return 200 "..." "...";
    }

}
</code></pre>
<p>Validate and reload the configuration, and from now on only messages with a level of <code>warn</code> or above will be logged.</p>
<pre><code class="lang-shell">cat /var/log/nginx/error.log

# 2021/04/25 11:27:02 [emerg] 12769#12769: invalid number of arguments in "return" directive in /etc/nginx/nginx.conf:16
</code></pre>
<p>Unlike the previous output, there are no <code>notice</code> entries here. <code>emerg</code> is a higher level error than <code>warn</code> and that's why it has been logged.</p>
<p>For most projects, leaving the error configuration as it is should be fine. The only suggestion I have is to set the minimum error level to <code>warn</code>. This way you won't have to look at unnecessary entries in the error log. </p>
<p>But if you want to learn more about customizing logging in NGINX, this <a target="_blank" href="https://docs.nginx.com/nginx/admin-guide/monitoring/logging/">link</a> to the official docs may help.</p>
<h2 id="heading-how-to-use-nginx-as-a-reverse-proxy">How to Use NGINX as a Reverse Proxy</h2>
<p>When configured as a reverse proxy, NGINX sits between the client and a back end server. The client sends requests to NGINX, then NGINX passes the request to the back end.</p>
<p>Once the back end server finishes processing the request, it sends it back to NGINX. In turn, NGINX returns the response to the client. </p>
<p>During the whole process, the client doesn't have any idea about who's actually processing the request. It sounds complicated in writing, but once you do it for yourself you'll see how easy NGINX makes it.</p>
<p>Let's see a very basic and impractical example of a reverse proxy:</p>
<pre><code class="lang-conf">events {

}

http {

    include /etc/nginx/mime.types;

    server {
        listen 80;
        server_name nginx.test;

        location / {
                proxy_pass "https://nginx.org/";
        }
    }
}
</code></pre>
<p>Apart from validating and reloading the configuration, you'll also have to add this address to your <code>hosts</code> file to make this demo work on your system:</p>
<pre><code class="lang-hosts">192.168.20.20   nginx.test
</code></pre>
<p>Now if you visit http://nginx.test, you'll be greeted by the original <a target="_blank" href="https://nginx.org">https://nginx.org</a> site while the URI remains unchanged.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/nginx-org-proxy.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You should be even able to navigate around the site to an extent. If you visit http://nginx.test/en/docs/ you should get the <a target="_blank" href="http://nginx.org/en/docs/">http://nginx.org/en/docs/</a> page in response.</p>
<p>So as you can see, at a basic level, the <code>proxy_pass</code> directive simply passes a client's request to a third party server and reverse proxies the response to the client.</p>
<h3 id="heading-nodejs-with-nginx">Node.js With NGINX</h3>
<p>Now that you know how to configure a basic reverse proxy server, you can serve a Node.js application reverse proxied by NGINX. I've added a demo application inside the repository that comes with this article.</p>
<blockquote>
<p>I'm assuming that you have experience with Node.js and know how to start a Node.js application using PM2.</p>
</blockquote>
<p>If you've already cloned the repository inside <code>/srv/nginx-handbook-projects</code> then the <code>node-js-demo</code> project should be available in the <code>/srv/nginx-handbook-projects/node-js-demo</code> directory.</p>
<p>For this demo to work, you'll need to install Node.js on your server. You can do that following the instructions found <a target="_blank" href="https://github.com/nodesource/distributions#debinstall">here</a>.</p>
<p>The demo application is a simple HTTP server that responds with a 200 status code and a JSON payload. You can start the application by simply executing <code>node app.js</code> but a better way is to use <a target="_blank" href="https://pm2.keymetrics.io">PM2</a>.</p>
<p>For those of you who don't know, PM2 is a daemon process manager widely used in production for Node.js applications. If you want to learn more, this <a target="_blank" href="https://pm2.keymetrics.io/docs/usage/quick-start/">link</a> may help.</p>
<p>Install PM2 globally by executing <code>sudo npm install -g pm2</code>. After the installation is complete, execute following command while being inside the <code>/srv/nginx-handbook-projects/node-js-demo</code> directory:</p>
<pre><code class="lang-shell">pm2 start app.js

# [PM2] Process successfully started
# ┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
# │ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
# ├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
# │ 0  │ app                │ fork     │ 0    │ online    │ 0%       │ 21.2mb   │
# └────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
</code></pre>
<p>Alternatively you can also do <code>pm2 start /srv/nginx-handbook-projects/node-js-demo/app.js</code> from anywhere on the server. You can stop the application by executing the <code>pm2 stop app</code> command.</p>
<p>The application should be running now but should not be accessible from outside of the server. To verify if the application is running or not, send a get request to http://localhost:3000 from inside your server:</p>
<pre><code class="lang-shell">curl -i localhost:3000

# HTTP/1.1 200 OK
# X-Powered-By: Express
# Content-Type: application/json; charset=utf-8
# Content-Length: 62
# ETag: W/"3e-XRN25R5fWNH2Tc8FhtUcX+RZFFo"
# Date: Sat, 24 Apr 2021 12:09:55 GMT
# Connection: keep-alive
# Keep-Alive: timeout=5

# { "status": "success", "message": "You're reading The NGINX Handbook!" }
</code></pre>
<p>If you get a 200 response, then the server is running fine. Now to configure NGINX as a reverse proxy, open your configuration file and update its content as follows:</p>
<pre><code class="lang-conf">events {

}

http {
    listen 80;
    server_name nginx-handbook.test

    location / {
        proxy_pass http://localhost:3000;
    }
}
</code></pre>
<p>Nothing new to explain here. You're just passing the received request to the Node.js application running at port 3000. Now if you send a request to the server from outside you should get a response as follows:</p>
<pre><code class="lang-shell">curl -i http://nginx-handbook.test

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Sat, 24 Apr 2021 14:58:01 GMT
# Content-Type: application/json
# Transfer-Encoding: chunked
# Connection: keep-alive

# { "status": "success", "message": "You're reading The NGINX Handbook!" }
</code></pre>
<p>Although this works for a basic server like this, you may have to add a few more directives to make it work in a real world scenario depending on your application's requirements. </p>
<p>For example, if your application handles web socket connections, then you should update the configuration as follows:</p>
<pre><code class="lang-conf">events {

}

http {
    listen 80;
    server_name nginx-handbook.test

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
    }
}
</code></pre>
<p>The <code>proxy_http_version</code> directive sets the HTTP version for the server. By default it's 1.0, but web socket requires it to be at least 1.1. The <code>proxy_set_header</code> directive is used for setting a header on the back-end server. Generic syntax for this directive is as follows:</p>
<pre><code class="lang-conf">proxy_set_header &lt;header name&gt; &lt;header value&gt;
</code></pre>
<p>So, by writing <code>proxy_set_header Upgrade $http_upgrade;</code> you're instructing NGINX to pass the value of the <code>$http_upgrade</code> variable as a header named <code>Upgrade</code> – same for the <code>Connection</code> header. </p>
<p>If you would like to learn more about web socket proxying, this <a target="_blank" href="https://nginx.org/en/docs/http/websocket.html">link</a> to the official NGINX docs may help.</p>
<p>Depending on the headers required by your application, you may have to set more of them. But the above mentioned configuration is very commonly used to serve Node.js applications.</p>
<h3 id="heading-php-with-nginx">PHP With NGINX</h3>
<p>PHP and NGINX go together like bread and butter. After all the E and the P in the LEMP stack stand for NGINX and PHP.</p>
<blockquote>
<p>I'm assuming you have experience with PHP and know how to run a PHP application.</p>
</blockquote>
<p>I've already included a demo PHP application in the repository that comes with this article. If you've already cloned it in the <code>/srv/nginx-handbook-projects</code> directory, then the application should be inside <code>/srv/nginx-handbook-projects/php-demo</code>.</p>
<p>For this demo to work, you'll have to install a package called PHP-FPM. To install the package, you can execute following command:</p>
<pre><code class="lang-shell">sudo apt install php-fpm -y
</code></pre>
<p>To test out the application, start a PHP server by executing the following command while inside the <code>/srv/nginx-handbook-projects/php-demo</code> directory:</p>
<pre><code class="lang-shell">php -S localhost:8000

# [Sat Apr 24 16:17:36 2021] PHP 7.4.3 Development Server (http://localhost:8000) started
</code></pre>
<p>Alternatively you can also do <code>php -S localhost:8000 /srv/nginx-handbook-projects/php-demo/index.php</code> from anywhere on the server.</p>
<p>The application should be running at port 8000 but it can not be accessed from the outside of the server. To verify, send a get request to http://localhost:8000 from inside your server:</p>
<pre><code class="lang-shell">curl -I localhost:8000

# HTTP/1.1 200 OK
# Host: localhost:8000
# Date: Sat, 24 Apr 2021 16:22:42 GMT
# Connection: close
# X-Powered-By: PHP/7.4.3
# Content-type: application/json

# {"status":"success","message":"You're reading The NGINX Handbook!"}
</code></pre>
<p>If you get a 200 response then the server is running fine. Just like the Node.js configuration, now you can simply <code>proxy_pass</code> the requests to localhost:8000 – but with PHP, there is a better way.</p>
<p>The FPM part in PHP-FPM stands for FastCGI Process Module. FastCGI is a protocol just like HTTP for exchanging binary data. This protocol is slightly faster than HTTP and provides better security. </p>
<p>To use FastCGI instead of HTTP, update your configuration as follows:</p>
<pre><code class="lang-conf">events {

}

http {

      include /etc/nginx/mime.types;

      server {

          listen 80;
          server_name nginx-handbook.test;
          root /srv/nginx-handbook-projects/php-demo;

          index index.php;

          location / {
              try_files $uri $uri/ =404;
          }

          location ~ \.php$ {
              fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
              fastcgi_param REQUEST_METHOD $request_method;
              fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
      }
   }
}
</code></pre>
<p>Let's begin with the new <code>index</code> directive. As you know, NGINX by default looks for an index.html file to serve. But in the demo-project, it's called index.php. So by writing <code>index index.php</code>, you're instructing NGINX to use the index.php file as root instead. </p>
<p>This directive can accept multiple parameters. If you write something like <code>index index.php index.html</code>, NGINX will first look for index.php. If it doesn't find that file, it will look for an index.html file.</p>
<p>The <code>try_files</code> directive inside the first <code>location</code> context is the same as you've seen in a previous section. The <code>=404</code> at the end indicates the error to throw if none of the files are found.</p>
<p>The second <code>location</code> block is the place where the main magic happens. As you can see, we've replaced the <code>proxy_pass</code> directive by a new <code>fastcgi_pass</code>. As the name suggests, it's used to pass a request to a FastCGI service.</p>
<p>The PHP-FPM service by default runs on port 9000 of the host. So instead of using a Unix socket like I've done here, you can pass the request to <code>http://localhost:9000</code> directly. But using a Unix socket is more secure.</p>
<p>If you have multiple PHP-FPM versions installed, you can simply list all the socket file locations by executing the following command:</p>
<pre><code class="lang-shell">sudo find / -name *fpm.sock

# /run/php/php7.4-fpm.sock
# /run/php/php-fpm.sock
# /etc/alternatives/php-fpm.sock
# /var/lib/dpkg/alternatives/php-fpm.sock
</code></pre>
<p>The <code>/run/php/php-fpm.sock</code> file refers to the latest version of PHP-FPM installed on your system. I prefer using the one with the version number. This way even if PHP-FPM gets updated, I'll be certain about the version I'm using.</p>
<p>Unlike passing requests through HTTP, passing requests through FPM requires us to pass some extra information. </p>
<p>The general way of passing extra information to the FPM service is using the <code>fastcgi_param</code> directive. At the very least, you'll have to pass the request method and the script name to the back-end service for the proxying to work. </p>
<p>The <code>fastcgi_param REQUEST_METHOD $request_method;</code> passes the request method to the back-end and the <code>fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;</code> line passes the exact location of the PHP script to run.</p>
<p>At this state, your configuration should work. To test it out, visit your server and you should be greeted by something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/500-on-fastcgi.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Well, that's weird. A 500 error means NGINX has crashed for some reason. This is where the error logs can come in handy. Let's have a look at the last entry in the error.log file:</p>
<pre><code class="lang-shell">tail -n 1 /var/log/nginx/error.log

# 2021/04/24 17:15:17 [crit] 17691#17691: *21 connect() to unix:/var/run/php/php7.4-fpm.sock failed (13: Permission denied) while connecting to upstream, client: 192.168.20.20, server: nginx-handbook.test, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/var/run/php/php7.4-fpm.sock:", host: "nginx-handbook.test"
</code></pre>
<p>Seems like the NGINX process is being denied permission to access the PHP-FPM process.</p>
<p>One of the main reasons for getting a permission denied error is user mismatch. Have a look at the user owning the NGINX worker process.</p>
<pre><code class="lang-shell">ps aux | grep nginx

# root         677  0.0  0.4   8892  4260 ?        Ss   14:31   0:00 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
# nobody     17691  0.0  0.3   9328  3452 ?        S    17:09   0:00 nginx: worker process
# vagrant    18224  0.0  0.2   8160  2552 pts/0    S+   17:19   0:00 grep --color=auto nginx
</code></pre>
<p>As you can see, the process is currently owned by <code>nobody</code>. Now inspect the PHP-FPM process.</p>
<pre><code class="lang-shell"># ps aux | grep php

# root       14354  0.0  1.8 195484 18924 ?        Ss   16:11   0:00 php-fpm: master process (/etc/php/7.4/fpm/php-fpm.conf)
# www-data   14355  0.0  0.6 195872  6612 ?        S    16:11   0:00 php-fpm: pool www
# www-data   14356  0.0  0.6 195872  6612 ?        S    16:11   0:00 php-fpm: pool www
# vagrant    18296  0.0  0.0   8160   664 pts/0    S+   17:20   0:00 grep --color=auto php
</code></pre>
<p>This process, on the other hand, is owned by the <code>www-data</code> user. This is why NGINX is being denied access to this process.</p>
<p>To solve this issue, update your configuration as follows:</p>
<pre><code class="lang-conf">user www-data;

events {

}

http {

      include /etc/nginx/mime.types;

      server {

          listen 80;
          server_name nginx-handbook.test;
          root /srv/nginx-handbook-projects/php-demo;

          index index.php;

          location / {
              try_files $uri $uri/ =404;
          }

          location ~ \.php$ {
              fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
              fastcgi_param REQUEST_METHOD $request_method;
              fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
      }
   }
}
</code></pre>
<p>The <code>user</code> directive is responsible for setting the owner for the NGINX worker processes. Now inspect the the NGINX process once again:</p>
<pre><code class="lang-shell"># ps aux | grep nginx

# root         677  0.0  0.4   8892  4264 ?        Ss   14:31   0:00 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
# www-data   20892  0.0  0.3   9292  3504 ?        S    18:10   0:00 nginx: worker process
# vagrant    21294  0.0  0.2   8160  2568 pts/0    S+   18:18   0:00 grep --color=auto nginx
</code></pre>
<p>Undoubtedly the process is now owned by the <code>www-data</code> user. Send a request to your server to check if it's working or not:</p>
<pre><code class="lang-shell"># curl -i http://nginx-handbook.test

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Sat, 24 Apr 2021 18:22:24 GMT
# Content-Type: application/json
# Transfer-Encoding: chunked
# Connection: keep-alive

# {"status":"success","message":"You're reading The NGINX Handbook!"}
</code></pre>
<p>If you get a 200 status code with a JSON payload, you're good to go. </p>
<p>This simple configuration is fine for the demo application, but in real-life projects you'll have to pass some additional parameters.</p>
<p>For this reason, NGINX includes a partial configuration called <code>fastcgi_params</code>. This file contains a list of the most common FastCGI parameters.</p>
<pre><code class="lang-shell">cat /etc/nginx/fastcgi_params

# fastcgi_param  QUERY_STRING       $query_string;
# fastcgi_param  REQUEST_METHOD     $request_method;
# fastcgi_param  CONTENT_TYPE       $content_type;
# fastcgi_param  CONTENT_LENGTH     $content_length;

# fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
# fastcgi_param  REQUEST_URI        $request_uri;
# fastcgi_param  DOCUMENT_URI       $document_uri;
# fastcgi_param  DOCUMENT_ROOT      $document_root;
# fastcgi_param  SERVER_PROTOCOL    $server_protocol;
# fastcgi_param  REQUEST_SCHEME     $scheme;
# fastcgi_param  HTTPS              $https if_not_empty;

# fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
# fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

# fastcgi_param  REMOTE_ADDR        $remote_addr;
# fastcgi_param  REMOTE_PORT        $remote_port;
# fastcgi_param  SERVER_ADDR        $server_addr;
# fastcgi_param  SERVER_PORT        $server_port;
# fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
# fastcgi_param  REDIRECT_STATUS    200;
</code></pre>
<p>As you can see, this file also contains the <code>REQUEST_METHOD</code> parameter. Instead of passing that manually, you can just include this file in your configuration:</p>
<pre><code class="lang-conf">user www-data;

events {

}

http {

      include /etc/nginx/mime.types;

      server {

          listen 80;
          server_name nginx-handbook.test;
          root /srv/nginx-handbook-projects/php-demo;

          index index.php;

          location / {
              try_files $uri $uri/ =404;
          }

          location ~ \.php$ {
              fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
              fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
              include /etc/nginx/fastcgi_params;
      }
   }
}
</code></pre>
<p>Your server should behave just the same. Apart from the <code>fastcgi_params</code> file, you may also come across the <code>fastcgi.conf</code> file which contains a slightly different set of parameters. I would suggest that you avoid that due to some inconsistencies with its behavior.</p>
<h2 id="heading-how-to-use-nginx-as-a-load-balancer">How to Use NGINX as a Load Balancer</h2>
<p>Thanks to the reverse proxy design of NGINX, you can easily configure it as a load balancer.</p>
<p>I've already added a demo to the repository that comes with this article. If you've already cloned the repository inside the <code>/srv/nginx-handbook-projects/</code> directory then the demo should be in the <code>/srv/nginx-handbook-projects/load-balancer-demo/</code> directory.</p>
<p>In a real life scenario, load balancing may be required on large scale projects distributed across multiple servers. But for this simple demo, I've created three very simple Node.js servers responding with a server number and 200 status code.</p>
<p>For this demo to work, you'll need Node.js installed on the server. You can find instructions in this <a target="_blank" href="https://github.com/nodesource/distributions#debinstall">link</a> to help you get it installed.</p>
<p>Apart from this, you'll also need <a target="_blank" href="https://pm2.keymetrics.io/">PM2</a> for daemonizing the Node.js servers provided in this demo.</p>
<p>If you haven't already, install PM2 by executing <code>sudo npm install -g pm2</code>. After the installation finishes, execute the following commands to start the three Node.js servers:</p>
<pre><code class="lang-shell">pm2 start /srv/nginx-handbook-projects/load-balancer-demo/server-1.js

pm2 start /srv/nginx-handbook-projects/load-balancer-demo/server-2.js

pm2 start /srv/nginx-handbook-projects/load-balancer-demo/server-3.js

pm2 list

# ┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
# │ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
# ├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
# │ 0  │ server-1           │ fork     │ 0    │ online    │ 0%       │ 37.4mb   │
# │ 1  │ server-2           │ fork     │ 0    │ online    │ 0%       │ 37.2mb   │
# │ 2  │ server-3           │ fork     │ 0    │ online    │ 0%       │ 37.1mb   │
# └────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
</code></pre>
<p>Three Node.js servers should be running on localhost:3001, localhost:3002, localhost:3003 respectively.</p>
<p>Now update your configuration as follows:</p>
<pre><code class="lang-conf">events {

}

http {

    upstream backend_servers {
        server localhost:3001;
        server localhost:3002;
        server localhost:3003;
    }

    server {

        listen 80;
        server_name nginx-handbook.test;

        location / {
            proxy_pass http://backend_servers;
        }
    }
}
</code></pre>
<p>The configuration inside the <code>server</code> context is the same as you've already seen. The <code>upstream</code> context, though, is new. An upstream in NGINX is a collection of servers that can be treated as a single backend.</p>
<p>So the three servers you started using PM2 can be put inside a single upstream and you can let NGINX balance the load between them.</p>
<p>To test out the configuration, you'll have to send a number of requests to the server. You can automate the process using a <code>while</code> loop in bash:</p>
<pre><code class="lang-shell">while sleep 0.5; do curl http://nginx-handbook.test; done

# response from server - 2.
# response from server - 3.
# response from server - 1.
# response from server - 2.
# response from server - 3.
# response from server - 1.
# response from server - 2.
# response from server - 3.
# response from server - 1.
# response from server - 2.
</code></pre>
<p>You can cancel the loop by hitting <code>Ctrl + C</code> on your keyboard. As you can see from the responses from the server, NGINX is load balancing the servers automatically.</p>
<p>Of course, depending on the project scale, load balancing can be a lot more complicated than this. But the goal of this article is to get you started, and I believe you now have a basic understanding of load balancing with NGINX. You can stop the three running server by executing <code>pm2 stop server-1 server-2 server-3</code> command (and it's a good idea here).</p>
<h2 id="heading-how-to-optimize-nginx-for-maximum-performance">How to Optimize NGINX for Maximum Performance</h2>
<p>In this section of the article, you'll learn about a number of ways to get the maximum performance from your server. </p>
<p>Some of these methods will be application-specific, which means they'll probably need tweaking considering your application requirements. But some of them will be general optimization techniques.</p>
<p>Just like the previous sections, changes in configuration will be frequesnt in this one, so don't forget to validate and reload your configuration file every time.</p>
<h3 id="heading-how-to-configure-worker-processes-and-worker-connections">How to Configure Worker Processes and Worker Connections</h3>
<p>As I've already mentioned in a previous section, NGINX can spawn multiple worker processes capable of handling thousands of requests each.</p>
<pre><code class="lang-shell">sudo systemctl status nginx

# ● nginx.service - A high performance web server and a reverse proxy server
#      Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
#      Active: active (running) since Sun 2021-04-25 08:33:11 UTC; 5h 45min ago
#        Docs: man:nginx(8)
#    Main PID: 3904 (nginx)
#       Tasks: 2 (limit: 1136)
#      Memory: 3.2M
#      CGroup: /system.slice/nginx.service
#              ├─ 3904 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
#              └─16443 nginx: worker process
</code></pre>
<p>As you can see, right now there is only one NGINX worker process on the system. This number, however, can be changed by making a small change to the configuration file.</p>
<pre><code class="lang-conf">worker_processes 2;

events {

}

http {

    server {

        listen 80;
        server_name nginx-handbook.test;

        return 200 "worker processes and worker connections configuration!\n";
    }
}
</code></pre>
<p>The <code>worker_process</code> directive written in the <code>main</code> context is responsible for setting the number of worker processes to spawn. Now check the NGINX service once again and you should see two worker processes:</p>
<pre><code class="lang-shell">sudo systemctl status nginx

# ● nginx.service - A high performance web server and a reverse proxy server
#      Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
#      Active: active (running) since Sun 2021-04-25 08:33:11 UTC; 5h 54min ago
#        Docs: man:nginx(8)
#     Process: 22610 ExecReload=/usr/sbin/nginx -g daemon on; master_process on; -s reload (code=exited, status=0/SUCCESS)
#    Main PID: 3904 (nginx)
#       Tasks: 3 (limit: 1136)
#      Memory: 3.7M
#      CGroup: /system.slice/nginx.service
#              ├─ 3904 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
#              ├─22611 nginx: worker process
#              └─22612 nginx: worker process
</code></pre>
<p>Setting the number of worker processes is easy, but determining the optimal number of worker processes requires a bit more work.</p>
<p>The worker processes are asynchronous in nature. This means that they will process incoming requests as fast as the hardware can. </p>
<p>Now consider that your server runs on a single core processor. If you set the number of worker processes to 1, that single process will utilize 100% of the CPU capacity. But if you set it to 2, the two processes will be able to utilize 50% of the CPU each. So increasing the number of worker processes doesn't mean better performance.</p>
<p>A rule of thumb in determining the optimal number of worker processes is <strong>number of worker process = number of CPU cores</strong>.</p>
<p>If you're running on a server with a dual core CPU, the number of worker processes should be set to 2. In a quad core it should be set to 4...and you get the idea.</p>
<p>Determining the number of CPUs on your server is very easy on Linux.</p>
<pre><code class="lang-shell">nproc

# 1
</code></pre>
<p>I'm on a single CPU virtual machine, so the <code>nproc</code> detects that there's one CPU. Now that you know the number of CPUs, all that is left to do is set the number on the configuration.</p>
<p>That's all well and good, but every time you upscale the server and the CPU number changes, you'll have to update the server configuration manually.</p>
<p>NGINX provides a better way to deal with this issue. You can simply set the number of worker processes to <code>auto</code> and NGINX will set the number of processes based on the number of CPUs automatically.</p>
<pre><code class="lang-conf">worker_processes auto;

events {

}

http {

    server {

        listen 80;
        server_name nginx-handbook.test;

        return 200 "worker processes and worker connections configuration!\n";
    }
}
</code></pre>
<p>Inspect the NGINX process once again:</p>
<pre><code class="lang-shell">sudo systemctl status nginx

# ● nginx.service - A high performance web server and a reverse proxy server
#      Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
#      Active: active (running) since Sun 2021-04-25 08:33:11 UTC; 6h ago
#        Docs: man:nginx(8)
#     Process: 22610 ExecReload=/usr/sbin/nginx -g daemon on; master_process on; -s reload (code=exited, status=0/SUCCESS)
#    Main PID: 3904 (nginx)
#       Tasks: 2 (limit: 1136)
#      Memory: 3.2M
#      CGroup: /system.slice/nginx.service
#              ├─ 3904 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
#              └─23659 nginx: worker process
</code></pre>
<p>The number of worker processes is back to one again, because that's what is optimal for this server.</p>
<p>Apart from the worker processes there is also the worker connection, indicating the highest number of connections a single worker process can handle. </p>
<p>Just like the number of worker processes, this number is also related to the number of your CPU core and the number of files your operating system is allowed to open per core.</p>
<p>Finding out this number is very easy on Linux:</p>
<pre><code class="lang-shell">ulimit -n

# 1024
</code></pre>
<p>Now that you have the number, all that is left is to set it in the configuration:</p>
<pre><code class="lang-conf">worker_processes auto;

events {
    worker_connections 1024;
}

http {

    server {

        listen 80;
        server_name nginx-handbook.test;

        return 200 "worker processes and worker connections configuration!\n";
    }
}
</code></pre>
<p>The <code>worker_connections</code> directive is responsible for setting the number of worker connections in a configuration. This is also the first time you're working with the <code>events</code> context. </p>
<p>In a previous section, I mentioned that this context is used for setting values used by NGINX on a general level. The worker connections configuration is one such example.</p>
<h3 id="heading-how-to-cache-static-content">How to Cache Static Content</h3>
<p>The second technique for optimizing your server is caching static content. Regardless of the application you're serving, there is always a certain amount of static content being served, such as stylesheets, images, and so on.</p>
<p>Considering that this content is not likely to change very frequently, it's a good idea to cache them for a certain amount of time. NGINX makes this task easy as well.</p>
<pre><code class="lang-conf">worker_processes auto;

events {
    worker_connections 1024;
}

http {

    include /env/nginx/mime.types;

    server {

        listen 80;
        server_name nginx-handbook.test;

        root /srv/nginx-handbook-demo/static-demo;

        location ~* \.(css|js|jpg)$ {
            access_log off;

            add_header Cache-Control public;
            add_header Pragma public;
            add_header Vary Accept-Encoding;
            expires 1M;
        }
    }
}
</code></pre>
<p>By writing <code>location ~* .(css|js|jpg)$</code> you're instructing NGINX to match requests asking for a file ending with <code>.css</code>, <code>.js</code> and <code>.jpg</code>. </p>
<p>In my applications, I usually store images in the <a target="_blank" href="https://developers.google.com/speed/webp">WebP</a> format even if the user submits a different format. This way, configuring the static cache becomes even easier for me.</p>
<p>You can use the <code>add_header</code> directive to include a header in the response to the client. Previously you've seen the <code>proxy_set_header</code> directive used for setting headers on an ongoing request to the backend server. The <code>add_header</code> directive on the other hand only adds a given header to the response.</p>
<p>By setting the <code>Cache-Control</code> header to public, you're telling the client that this content can be cached in any way. The <code>Pragma</code> header is just an older version of the <code>Cache-Control</code> header and does more or less the same thing. </p>
<p>The next header, <code>Vary</code>, is responsible for letting the client know that this cached content may vary. </p>
<p>The value of <code>Accept-Encoding</code> means that the content may vary depending on the content encoding accepted by the client. This will be clarified further in the next section.</p>
<p>Finally the <code>expires</code> directive allows you to set the <code>Expires</code> header conveniently. The <code>expires</code> directive takes the duration of time this cache will be valid. By setting it to <code>1M</code> you're telling NGINX to cache the content for one month. You can also set this to <code>10m</code> or 10 minutes, <code>24h</code> or 24 hours, and so on.</p>
<p>Now to test out the configuration, sent a request for the the-nginx-handbook.jpg file from the server:</p>
<pre><code class="lang-shell">curl -I http://nginx-handbook.test/the-nginx-handbook.jpg

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Sun, 25 Apr 2021 15:58:22 GMT
# Content-Type: image/jpeg
# Content-Length: 19209
# Last-Modified: Sun, 25 Apr 2021 08:35:33 GMT
# Connection: keep-alive
# ETag: "608529d5-4b09"
# Expires: Tue, 25 May 2021 15:58:22 GMT
# Cache-Control: max-age=2592000
# Cache-Control: public
# Pragma: public
# Vary: Accept-Encoding
# Accept-Ranges: bytes
</code></pre>
<p>As you can see, the headers have been added to the response and any modern browser should be able to interpret them.</p>
<h3 id="heading-how-to-compress-responses">How to Compress Responses</h3>
<p>The final optimization technique that I'm going to show today is a pretty straightforward one: compressing responses to reduce their size.</p>
<pre><code class="lang-conf">worker_processes auto;

events {
    worker_connections 1024;
}

http {
    include /env/nginx/mime.types;

    gzip on;
    gzip_comp_level 3;

    gzip_types text/css text/javascript;

    server {

        listen 80;
        server_name nginx-handbook.test;

        root /srv/nginx-handbook-demo/static-demo;

        location ~* \.(css|js|jpg)$ {
            access_log off;

            add_header Cache-Control public;
            add_header Pragma public;
            add_header Vary Accept-Encoding;
            expires 1M;
        }
    }
}
</code></pre>
<p>If you're not already familiar with it, <a target="_blank" href="https://www.gnu.org/software/gzip/">GZIP</a> is a popular file format used by applications for file compression and decompression. NGINX can utilize this format to compress responses using the <code>gzip</code> directives.</p>
<p>By writing <code>gzip on</code> in the <code>http</code> context, you're instructing NGINX to compress responses. The <code>gzip_comp_level</code> directive sets the level of compression. You can set it to a very high number, but that doesn't guarantee better compression. Setting a number between 1 - 4 gives you an efficient result. For example, I like setting it to 3.</p>
<p>By default, NGINX compresses HTML responses. To compress other file formats, you'll have to pass them as parameters to the <code>gzip_types</code> directive. By writing <code>gzip_types text/css text/javascript;</code> you're telling NGINX to compress any file with the mime types of text/css and text/javascript.</p>
<p>Configuring compression in NGINX is not enough. The client has to ask for the compressed response instead of the uncompressed responses. I hope you remember the <code>add_header Vary Accept-Encoding;</code> line in the previous section on caching. This header lets the client know that the response may vary based on what the client accepts.</p>
<p>As an example, if you want to request the uncompressed version of the mini.min.css file from the server, you may do something like this:</p>
<pre><code class="lang-shell">curl -I http://nginx-handbook.test/mini.min.css

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Sun, 25 Apr 2021 16:30:32 GMT
# Content-Type: text/css
# Content-Length: 46887
# Last-Modified: Sun, 25 Apr 2021 08:35:33 GMT
# Connection: keep-alive
# ETag: "608529d5-b727"
# Expires: Tue, 25 May 2021 16:30:32 GMT
# Cache-Control: max-age=2592000
# Cache-Control: public
# Pragma: public
# Vary: Accept-Encoding
# Accept-Ranges: bytes
</code></pre>
<p>As you can see, there's nothing about compression. Now if you want to ask for the compressed version of the file, you'll have to send an additional header.</p>
<pre><code class="lang-shell">curl -I -H "Accept-Encoding: gzip" http://nginx-handbook.test/mini.min.css

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Sun, 25 Apr 2021 16:31:38 GMT
# Content-Type: text/css
# Last-Modified: Sun, 25 Apr 2021 08:35:33 GMT
# Connection: keep-alive
# ETag: W/"608529d5-b727"
# Expires: Tue, 25 May 2021 16:31:38 GMT
# Cache-Control: max-age=2592000
# Cache-Control: public
# Pragma: public
# Vary: Accept-Encoding
# Content-Encoding: gzip
</code></pre>
<p>As you can see in the response headers, the <code>Content-Encoding</code> is now set to <code>gzip</code> meaning this is the compressed version of the file.</p>
<p>Now if you want to compare the difference in file size, you can do something like this:</p>
<pre><code class="lang-shell">cd ~
mkdir compression-test &amp;&amp; cd compression-test

curl http://nginx-handbook.test/mini.min.css &gt; uncompressed.css

curl -H "Accept-Encoding: gzip" http://nginx-handbook.test/mini.min.css &gt; compressed.css

ls -lh

# -rw-rw-r-- 1 vagrant vagrant 9.1K Apr 25 16:35 compressed.css
# -rw-rw-r-- 1 vagrant vagrant  46K Apr 25 16:35 uncompressed.css
</code></pre>
<p>The uncompressed version of the file is <code>46K</code> and the compressed version is <code>9.1K</code>, almost six times smaller. On real life sites where stylesheets can be much larger, compression can make your responses smaller and faster.</p>
<h2 id="heading-how-to-understand-the-main-configuration-file">How to Understand the Main Configuration File</h2>
<p>I hope you remember the original <code>nginx.conf</code> file you renamed in an earlier section. According to the <a target="_blank" href="https://wiki.debian.org/Nginx/DirectoryStructure">Debian wiki</a>, this file is meant to be changed by the NGINX maintainers and not by server administrators, unless they know exactly what they're doing.</p>
<p>But throughout the entire article, I've taught you to configure your servers in this very file. In this section, however, I'll who you how you should configure your servers without changing the <code>nginx.conf</code> file.</p>
<p>To begin with, first delete or rename your modified <code>nginx.conf</code> file and bring back the original one:</p>
<pre><code class="lang-shell">sudo rm /etc/nginx/nginx.conf

sudo mv /etc/nginx/nginx.conf.backup /etc/nginx/nginx.conf

sudo nginx -s reload
</code></pre>
<p>Now NGINX should go back to its original state. Let's have a look at the content of this file once again by executing the <code>sudo cat /etc/nginx/nginx.conf</code> file:</p>
<pre><code class="lang-conf">user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 768;
    # multi_accept on;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    # server_tokens off;

    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # SSL Settings
    ##

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;

    ##
    # Logging Settings
    ##

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ##
    # Gzip Settings
    ##

    gzip on;

    # gzip_vary on;
    # gzip_proxied any;
    # gzip_comp_level 6;
    # gzip_buffers 16 8k;
    # gzip_http_version 1.1;
    # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    ##
    # Virtual Host Configs
    ##

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}


#mail {
#    # See sample authentication script at:
#    # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
# 
#    # auth_http localhost/auth.php;
#    # pop3_capabilities "TOP" "USER";
#    # imap_capabilities "IMAP4rev1" "UIDPLUS";
# 
#    server {
#        listen     localhost:110;
#        protocol   pop3;
#        proxy      on;
#    }
# 
#    server {
#        listen     localhost:143;
#        protocol   imap;
#        proxy      on;
#    }
#}
</code></pre>
<p>You should now be able to understand this file without much trouble. On the main context <code>user www-data;</code>, the <code>worker_processes auto;</code> lines should be easily recognizable to you. </p>
<p>The line <code>pid /run/nginx.pid;</code> sets the process ID for the NGINX process and <code>include /etc/nginx/modules-enabled/*.conf;</code> includes any configuration file found on the <code>/etc/nginx/modules-enabled/</code> directory. </p>
<p>This directory is meant for NGINX dynamic modules. I haven't covered dynamic modules in this article so I'll skip that.</p>
<p>Now inside the the <code>http</code> context, under basic settings you can see some common optimization techniques applied. Here's what these techniques do:</p>
<ul>
<li><code>sendfile on;</code> disables buffering for static files.</li>
<li><code>tcp_nopush on;</code> allows sending response header in one packet.</li>
<li><code>tcp_nodelay on;</code> disables <a target="_blank" href="https://en.wikipedia.org/wiki/Nagle's_algorithm">Nagle's Algorithm</a> resulting in faster static file delivery.</li>
</ul>
<p>The <code>keepalive_timeout</code> directive indicates how long to keep a connection open and the <code>types_hash_maxsize</code> directive sets the size of the types hash map. It also includes the <code>mime.types</code> file by default.</p>
<p>I'll skip the SSL settings simply because we haven't covered them in this article. We've already discussed the logging and gzip settings. You may see some of the directives regarding gzip as commented. As long as you understand what you're doing, you may customize these settings.</p>
<p>You use the <code>mail</code> context to configure NGINX as a mail server. We've only talked about NGINX as a web server so far, so I'll skip this as well.</p>
<p>Now under the virtual hosts settings, you should see two lines as follows:</p>
<pre><code class="lang-conf">##
# Virtual Host Configs
##

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
</code></pre>
<p>These two lines instruct NGINX to include any configuration files found inside the <code>/etc/nginx/conf.d/</code> and <code>/etc/nginx/sites-enabled/</code> directories.</p>
<p>After seeing these two lines, people often take these two directories as the ideal place to put their configuration files, but that's not right.</p>
<p>There is another directory <code>/etc/nginx/sites-available/</code> that's meant to store configuration files for your virtual hosts. The <code>/etc/nginx/sites-enabled/</code> directory is meant for storing the symbolic links to the files from the <code>/etc/nginx/sites-available/</code> directory. </p>
<p>In fact there is an example configuration:</p>
<pre><code class="lang-shell">ln -lh /etc/nginx/sites-enabled/

# lrwxrwxrwx 1 root root 34 Apr 25 08:33 default -&gt; /etc/nginx/sites-available/default
</code></pre>
<p>As you can see, the directory contains a symbolic link to the <code>/etc/nginx/sites-available/default</code> file.</p>
<p>The idea is to write multiple virtual hosts inside the <code>/etc/nginx/sites-available/</code> directory and make some of them active by symbolic linking them to the <code>/etc/nginx/sites-enabled/</code> directory.</p>
<p>To demonstrate this concept, let's configure a simple static server. First, delete the default virtual host symbolic link, deactivating this configuration in the process:</p>
<pre><code class="lang-shell">sudo rm /etc/nginx/sites-enabled/default

ls -lh /etc/nginx/sites-enabled/

# lrwxrwxrwx 1 root root 41 Apr 25 18:01 nginx-handbook -&gt; /etc/nginx/sites-available/nginx-handbook
</code></pre>
<p>Create a new file by executing <code>sudo touch /etc/nginx/sites-available/nginx-handbook</code> and put the following content in there:</p>
<pre><code>server {
    listen <span class="hljs-number">80</span>;
    server_name nginx-handbook.test;

    root /srv/nginx-handbook-projects/<span class="hljs-keyword">static</span>-demo;
}
</code></pre><p>Files inside the <code>/etc/nginx/sites-available/</code> directory are meant to be included within the main <code>http</code> context so they should contain <code>server</code> blocks only.</p>
<p>Now create a symbolic link to this file inside the <code>/etc/nginx/sites-enabled/</code> directory by executing the following command:</p>
<pre><code class="lang-shell">sudo ln -s /etc/nginx/sites-available/nginx-handbook /etc/nginx/sites-enabled/nginx-handbook

ls -lh /etc/nginx/sites-enabled/

# lrwxrwxrwx 1 root root 34 Apr 25 08:33 default -&gt; /etc/nginx/sites-available/default
# lrwxrwxrwx 1 root root 41 Apr 25 18:01 nginx-handbook -&gt; /etc/nginx/sites-available/nginx-handbook
</code></pre>
<p>Before validating and reloading the configuration file, you'll have to reopen the log files. Otherwise you may get a permission denied error. This happens because the process ID is different this time as a result of swapping the old <code>nginx.conf</code> file.</p>
<pre><code class="lang-shell">sudo rm /var/log/nginx/*.log

sudo touch /var/log/nginx/access.log /var/log/nginx/error.log

sudo nginx -s reopen
</code></pre>
<p>Finally, validate and reload the configuration file:</p>
<pre><code>sudo nginx -t

# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

sudo nginx -s reload
</code></pre><p>Visit the server and you should be greeted with the good old The NGINX Handbook page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-100.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you've configured the server correctly and you're still getting the old NGINX welcome page, perform a hard refresh. The browser often holds on to old assets and requires a little cleanup.</p>
<h2 id="heading-how-to-configure-ssl-and-http2">How To Configure SSL and HTTP/2</h2>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> is the newest version of the wildly popular Hyper Text Transport Protocol. Based on Google's experimental <a target="_blank" href="https://en.wikipedia.org/wiki/SPDY">SPDY</a> protocol, HTTP/2 provides better performance by introducing features like full request and response multiplexing, better compression of header fields, server push and request prioritization.</p>
<p>Some of the notable features of HTTP/2 is as follows:</p>
<ol>
<li><strong>Binary Protocol</strong> - While HTTP/1.x was a text based protocol, HTTP/2 is a binary protocol resulting in less error during data transfer process.</li>
<li><strong>Multiplexed Streams</strong> - All HTTP/2 connections are multiplexed streams meaning multiple files can be transferred in a single stream of binary data.</li>
<li><strong>Compressed Header</strong> - HTTP/2 compresses header data in responses resulting in faster transfer of data.</li>
<li><strong>Server Push</strong> - This capability allows the server to send linked resources to the client automatically, greatly reducing the number of requests to the server.</li>
<li><strong>Stream Prioritization</strong> - HTTP/2 can prioritize data streams based on their type resulting in better bandwidth allocation where necessary.</li>
</ol>
<blockquote>
<p>If you want to learn more about the improvements in HTTP/2 this <a target="_blank" href="https://kinsta.com/learn/what-is-http2/">article</a> by <a target="_blank" href="https://kinsta.com/">Kinsta</a> may help.</p>
</blockquote>
<p>While a significant upgrade over its predecessor, HTTP/2 is not as widely adapted as it should have been. In this section, I'll introduce you to some of the new features mentioned previously and I'll also show you how to enable HTTP/2 on your NGINX powered web server.</p>
<p>For this section, I'll be using the <code>static-demo</code> project. I'm assuming you've already cloned the repository inside <code>/srv/nginx-handbook-projects</code> directory. If you haven't, this is the time to do so. Also, this section has to be done on a virtual private server instead of a virtual machine.</p>
<p>For simplicity, I'll use the <code>/etc/nginx/sites-available/default</code> file as my configuration. Open the file using <code>nano</code> or <code>vi</code> if you fancy that.</p>
<pre><code class="lang-shell">nano /etc/nginx/sites-available/default
</code></pre>
<p>Update the file's content as follows:</p>
<pre><code class="lang-conf">server {
        listen 80;
        server_name nginx-handbook.farhan.dev;  

        root /srv/nginx-handbook-projects/static-demo;
}
</code></pre>
<p>As you can see, the <code>/srv/nginx-handbook-projects/static-demo;</code> directory has been set as the root of this site and <code>nginx-handbook.farhan.dev</code> has been set as the server name. If you do not have a custom domain set up, you can use your server's IP address as the server name here.</p>
<pre><code class="lang-shell">nginx -t

# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

nginx -s reload
</code></pre>
<p>Test the configuration by executing <code>nginx -t</code> and reload the configuration by executing <code>nginx -s reload</code> commands.</p>
<p>Finally visit your server and you should be greeted with a simple static HTML page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/OCHoJEYdm.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>One of the pre-requisite to have HTTP/2 working on your server is to have a valid SSL certificate. Lets do that first.</p>
<h3 id="heading-how-to-configure-ssl">How To Configure SSL</h3>
<p>For those of you who may not know, an SSL certificate is what allows a server to make the move from HTTP to HTTPS. These certificates are issued by a certificate authority (CA). Most of the authorities charge a fee for issuing certificates but nonprofit authorities such as <a target="_blank" href="https://letsencrypt.org/">Let's Encrypt</a>, issues certificates for free.</p>
<blockquote>
<p>If you want to understand the theory of SSL in a bit more detail, this <a target="_blank" href="https://www.cloudflare.com/learning/ssl/what-is-an-ssl-certificate/">article</a> on the <a target="_blank" href="https://www.cloudflare.com/learning/">Cloudflare Learning Center</a> may help.</p>
</blockquote>
<p>Thanks to open-source tools like <a target="_blank" href="https://github.com/certbot/certbot">Certbot</a>, installing a free certificate is dead easy. Head over to <a target="_blank" href="https://certbot.eff.org/">certbot.eff.org</a> link. Now select the software and system that powers your server.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/A13UVsSsE.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I'm running NGINX on Ubuntu 20.04 and if you've been in line with this article, you should have the same combination.</p>
<p>After selecting your combination of software and system, you'll be forwarded to a new page containing step by step instructions for installing certbot and a new SSL certificate.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/27NSXHEp2.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The installation steps for certbot may differ from system to system but rest of the instructions should remain same. On Ubuntu, the recommended way is to use <a target="_blank" href="https://snapcraft.io/">snap</a>.</p>
<pre><code class="lang-shell">snap install --classic certbot

# certbot 1.14.0 from Certbot Project (certbot-eff✓) installed

certbot --version

# certbot 1.14.0
</code></pre>
<p>Certbot is now installed and ready to be used. Before you install a new certificate, make sure the NGINX configuration file contains all the necessary server names. Such as, if you want to install a new certificate for <code>yourdomain.tld</code> and <code>www.yourdomain.tld</code>, you'll have to include both of them in your configuration.</p>
<p>Once you're happy with your configuration, you can install a newly provisioned certificate for your server. To do so, execute the <code>certbot</code> program with <code>--nginx</code> option.</p>
<pre><code class="lang-shell">certbot --nginx

# Saving debug log to /var/log/letsencrypt/letsencrypt.log
# Plugins selected: Authenticator nginx, Installer nginx
# Enter email address (used for urgent renewal and security notices)
#  (Enter 'c' to cancel): shovik.is.here@gmail.com

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Please read the Terms of Service at
# https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
# agree in order to register with the ACME server. Do you agree?
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# (Y)es/(N)o: Y

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Would you be willing, once your first certificate is successfully issued, to
# share your email address with the Electronic Frontier Foundation, a founding
# partner of the Let's Encrypt project and the non-profit organization that
# develops Certbot? We'd like to send you email about our work encrypting the web,
# EFF news, campaigns, and ways to support digital freedom.
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# (Y)es/(N)o: N
# Account registered.

# Which names would you like to activate HTTPS for?
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# 1: nginx-handbook.farhan.dev
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Select the appropriate numbers separated by commas and/or spaces, or leave input
# blank to select all options shown (Enter 'c' to cancel): 
# Requesting a certificate for nginx-handbook.farhan.dev
# Performing the following challenges:
# http-01 challenge for nginx-handbook.farhan.dev
# Waiting for verification...
# Cleaning up challenges
# Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/default
# Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/default

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Congratulations! You have successfully enabled
# https://nginx-handbook.farhan.dev
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# IMPORTANT NOTES:
#  - Congratulations! Your certificate and chain have been saved at:
#    /etc/letsencrypt/live/nginx-handbook.farhan.dev/fullchain.pem
#    Your key file has been saved at:
#    /etc/letsencrypt/live/nginx-handbook.farhan.dev/privkey.pem
#    Your certificate will expire on 2021-07-30. To obtain a new or
#    tweaked version of this certificate in the future, simply run
#    certbot again with the "certonly" option. To non-interactively
#    renew *all* of your certificates, run "certbot renew"
#  - If you like Certbot, please consider supporting our work by:

#    Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
#    Donating to EFF:                    https://eff.org/donate-le
</code></pre>
<p>You'll be asked for an emergency contact email address, license agreement and if you would like to receive emails from them or not.</p>
<p>The certbot program will automatically read the server names from your configuration file and show you a list of them. If you have multiple virtual hosts on your server, certbot will recognize them as well.</p>
<p>Finally if the installation is successful, you'll be congratulated by the program. To verify if everything's working or not, visit your server with HTTPS this time:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/mCbapBf9n.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>As you can see, HTTPS has been enabled successfully and you can confirm that the certificate is verified by <a target="_blank" href="https://letsencrypt.org/">Let's Encrypt</a> authority. Later on, if you add new virtual hosts to this server with new domains or sub domains, you'll have to reinstall the certificates.</p>
<p>It's also possible to install wildcard certificate such as <code>*.yourdomain.tld</code> for some supported DNS managers. Detailed instructions can be found on the previously shown installation instruction page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/PLFcZoO8P.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>A newly installed certificate will be valid for 90 days. After that, a renewal will be required. Certbot does the renewal automatically. You can execute <code>certbot renew</code> command with the <code>--dry-run</code> option to test out the auto renewal feature.</p>
<pre><code class="lang-shell">certbot renew --dry-run

# Saving debug log to /var/log/letsencrypt/letsencrypt.log

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Processing /etc/letsencrypt/renewal/nginx-handbook.farhan.dev.conf
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Cert not due for renewal, but simulating renewal for dry run
# Plugins selected: Authenticator nginx, Installer nginx
# Account registered.
# Simulating renewal of an existing certificate for nginx-handbook.farhan.dev
# Performing the following challenges:
# http-01 challenge for nginx-handbook.farhan.dev
# Waiting for verification...
# Cleaning up challenges

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# new certificate deployed with reload of nginx server; fullchain is
# /etc/letsencrypt/live/nginx-handbook.farhan.dev/fullchain.pem
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Congratulations, all simulated renewals succeeded: 
#   /etc/letsencrypt/live/nginx-handbook.farhan.dev/fullchain.pem (success)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
</code></pre>
<p>The command will simulate a certificate renewal to test if it's correctly set up or not. If it succeeds you'll be congratulated by the program. This step ends the procedure of installing an SSL certificate on your server.</p>
<p>To understand what certbot did behind the scenes, open up the <code>/etc/nginx/sites-available/default</code> file once again and see how its content has been altered.</p>
<pre><code class="lang-conf">server {

    server_name nginx-handbook.farhan.dev;  

    root /srv/nginx-handbook-projects/static-demo;

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/nginx-handbook.farhan.dev/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/nginx-handbook.farhan.dev/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = nginx-handbook.farhan.dev) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;

    server_name nginx-handbook.farhan.dev;
    return 404; # managed by Certbot
}
</code></pre>
<p>As you can see, certbot has added quite a few lines here. I'll explain the notable ones.</p>
<pre><code class="lang-conf">server {
    # ...
    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    # ...
}
</code></pre>
<p>Like the 80 port, 443 is widely used for listening to HTTPS requests. By writing <code>listen 443 ssl;</code> certbot is instructing NGINX to listen for any HTTPS request on port 443. The <code>listen [::]:443 ssl ipv6only=on;</code> line is for handling IPV6 connections.</p>
<pre><code class="lang-conf">

server {
    # ...
    ssl_certificate /etc/letsencrypt/live/nginx-handbook.farhan.dev/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/nginx-handbook.farhan.dev/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    # ...
}
</code></pre>
<p>The <code>ssl_certificate</code> directive is used for indicating the location of the certificate and the private key file on your server. The <code>/etc/letsencrypt/options-ssl-nginx.conf;</code> includes some common directives necessary for SSL.</p>
<p>Finally the <code>ssl_dhparam</code> indicates to the file defining how OpenSSL is going to perform <a target="_blank" href="https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange">Diffie–Hellman key exchange</a>. If you want to learn more about the purpose of <code>/etc/letsencrypt/ssl-dhparams.pem;</code> file, this stack exchange <a target="_blank" href="https://security.stackexchange.com/questions/94390/whats-the-purpose-of-dh-parameters">thread</a> may help you.</p>
<pre><code class="lang-conf">server {
    if ($host = nginx-handbook.farhan.dev) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;

    server_name nginx-handbook.farhan.dev;
    return 404; # managed by Certbot
}
</code></pre>
<p>This newly added server block is responsible for redirecting any HTTP requests to HTTPS disabling HTTP access completely.</p>
<h3 id="heading-how-to-enable-http2">How To Enable HTTP/2</h3>
<p>Once you've successfully installed a valid SSL certificate on your server, you're ready to enable HTTP/2. SSL is a prerequisite for HTTP/2, so right off the bat you can see, security is not optional in HTTP/2.</p>
<p>HTTP/2 support for NGINX is provided by the <a target="_blank" href="https://nginx.org/en/docs/http/ngx_http_v2_module.html">ngx_http_v2_module</a> module. Pre-built binaries of NGINX on most of the systems come with this module baked in. If you've built NGINX from source however, you'll have to include this module manually.</p>
<p>Before upgrading to HTTP/2, send a request to your server and see the current protocol version.</p>
<pre><code class="lang-shell">curl -I -L https://nginx-handbook.farhan.dev

# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Date: Sat, 01 May 2021 10:46:36 GMT
# Content-Type: text/html
# Content-Length: 960
# Last-Modified: Fri, 30 Apr 2021 20:14:48 GMT
# Connection: keep-alive
# ETag: "608c6538-3c0"
# Accept-Ranges: bytes
</code></pre>
<p>As you can see, by default the server is on HTTP/1.1 protocol. On the next step, we'll update the configuration file as necessary for enabling HTTP/2.</p>
<p>To enable HTTP/2 on your server, open the <code>/etc/nginx/sites-available/default</code> file once again. Find wherever it says <code>listen [::]:443 ssl ipv6only=on;</code> or <code>listen 443 ssl;</code> and update them to <code>listen [::]:443 ssl http2 ipv6only=on;</code> and <code>listen 443 ssl http2;</code> respectively.</p>
<pre><code class="lang-conf">server {

    server_name nginx-handbook.farhan.dev;  

    root /srv/nginx-handbook-projects/static-demo;

    listen [::]:443 ssl http2 ipv6only=on; # managed by Certbot
    listen 443 ssl http2; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/nginx-handbook.farhan.dev/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/nginx-handbook.farhan.dev/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = nginx-handbook.farhan.dev) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;

    server_name nginx-handbook.farhan.dev;
    return 404; # managed by Certbot
}
</code></pre>
<p>Test the configuration file by executing <code>niginx -t</code> and reload the configuration by executing <code>nginx -s reload</code> commands. Now send a request to your server again.</p>
<pre><code class="lang-shell">curl -I -L https://nginx-handbook.farhan.dev

# HTTP/2 200 
# server: nginx/1.18.0 (Ubuntu)
# date: Sat, 01 May 2021 09:03:10 GMT
# content-type: text/html
# content-length: 960
# last-modified: Fri, 30 Apr 2021 20:14:48 GMT
# etag: "608c6538-3c0"
# accept-ranges: bytes
</code></pre>
<p>As you can see, HTTP/2 has been enabled for any client supporting the new protocol.</p>
<h3 id="heading-how-to-enable-server-push">How to Enable Server Push</h3>
<p>Server push is one of the many features that HTTP/2 brings to the table. Which means the server can push files to the client without the client having to request for them. In a HTTP/1.x server, a typical request for static content may look like as follows:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/f49aVyg9h.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>But on a server push enabled HTTP/2 server, it may look like as follows:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/wLod0KcsB.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>On a single request for the index.html file the server responds with the style.css file as well, minimizing the number of requests in the process.</p>
<p>In this section, I'll use an open-source HTTP client named <a target="_blank" href="https://nghttp2.org/">Nghttp2</a> for testing the server.</p>
<pre><code class="lang-shell">apt install nghttp2-client -y

# Reading package lists... Done
# Building dependency tree       
# Reading state information... Done
# The following additional packages will be installed:
#   libev4 libjansson4 libjemalloc2
# The following NEW packages will be installed:
#   libev4 libjansson4 libjemalloc2 nghttp2-client
# 0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded.
# Need to get 447 kB of archives.
# After this operation, 1,520 kB of additional disk space will be used.
# Get:1 http://archive.ubuntu.com/ubuntu focal/main amd64 libjansson4 amd64 2.12-1build1 [28.9 kB]
# Get:2 http://archive.ubuntu.com/ubuntu focal/universe amd64 libjemalloc2 amd64 5.2.1-1ubuntu1 [235 kB]
# Get:3 http://archive.ubuntu.com/ubuntu focal/universe amd64 libev4 amd64 1:4.31-1 [31.2 kB]
# Get:4 http://archive.ubuntu.com/ubuntu focal/universe amd64 nghttp2-client amd64 1.40.0-1build1 [152 kB]
# Fetched 447 kB in 1s (359 kB/s)     
# Selecting previously unselected package libjansson4:amd64.
# (Reading database ... 107613 files and directories currently installed.)
# Preparing to unpack .../libjansson4_2.12-1build1_amd64.deb ...
# Unpacking libjansson4:amd64 (2.12-1build1) ...
# Selecting previously unselected package libjemalloc2:amd64.
# Preparing to unpack .../libjemalloc2_5.2.1-1ubuntu1_amd64.deb ...
# Unpacking libjemalloc2:amd64 (5.2.1-1ubuntu1) ...
# Selecting previously unselected package libev4:amd64.
# Preparing to unpack .../libev4_1%3a4.31-1_amd64.deb ...
# Unpacking libev4:amd64 (1:4.31-1) ...
# Selecting previously unselected package nghttp2-client.
# Preparing to unpack .../nghttp2-client_1.40.0-1build1_amd64.deb ...
# Unpacking nghttp2-client (1.40.0-1build1) ...
# Setting up libev4:amd64 (1:4.31-1) ...
# Setting up libjemalloc2:amd64 (5.2.1-1ubuntu1) ...
# Setting up libjansson4:amd64 (2.12-1build1) ...
# Setting up nghttp2-client (1.40.0-1build1) ...
# Processing triggers for man-db (2.9.1-1) ...
# Processing triggers for libc-bin (2.31-0ubuntu9.2) ...

nghttp --version

# nghttp nghttp2/1.40.0
</code></pre>
<p>Lets test by sending a request to the server without server push.</p>
<pre><code class="lang-shell">nghttp --null-out --stat https://nginx-handbook.farhan.dev/index.html

# id  responseEnd requestStart  process code size request path
#  13      +836us       +194us    642us  200  492 /index.html

nghttp --null-out --stat --get-assets https://nginx-handbook.farhan.dev/index.html

# id  responseEnd requestStart  process code size request path
#  13      +836us       +194us    642us  200  492 /index.html
#  15     +3.11ms      +2.65ms    457us  200  45K /mini.min.css
#  17     +3.23ms      +2.65ms    578us  200  18K /the-nginx-handbook.jpg
</code></pre>
<p>On the first request <code>--null-out</code> means discard downloaded data and <code>--stat</code> means print statistics on terminal. On the second request <code>--get-assets</code> means also download assets such as stylesheets, images and scripts linked to this files. As a result you can tell by the <code>requestStart</code> times, the css file and image was downloaded shortly after the html file was downloaded.</p>
<p>Now, lets enable server push for stylesheets and images. Open <code>/etc/nginx/sites-available/default</code> file and update its content as follows:</p>
<pre><code class="lang-conf">server {

    server_name nginx-handbook.farhan.dev;

    root /srv/nginx-handbook-projects/static-demo;

    listen [::]:443 ssl http2 ipv6only=on; # managed by Certbot
    listen 443 ssl http2; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/nginx-handbook.farhan.dev/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/nginx-handbook.farhan.dev/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    location = /index.html {
        http2_push /mini.min.css;
        http2_push /the-nginx-handbook.jpg;
    }

    location = /about.html {
        http2_push /mini.min.css;
        http2_push /the-nginx-handbook.jpg;
    }

}
server {
    if ($host = nginx-handbook.farhan.dev) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;

    server_name nginx-handbook.farhan.dev;
    return 404; # managed by Certbot
}
</code></pre>
<p>Two location blocks have been added to exactly match <code>/index.html</code> and <code>/about.html</code> locations. The <code>http2_push</code> directive is used for sending back additional response. Now whenever NGINX receives a request for one of these two locations, it'll automatically send back the css and image file.</p>
<p>Test the configuration by executing <code>nginx -t</code> and reload the configuration by executing <code>nginx -s reload</code> commands.</p>
<pre><code class="lang-shell">nginx -t

# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

nginx -s reload
</code></pre>
<p>Now send another request to the server using <code>nghttp</code> and do not include <code>--get-assets</code> option.</p>
<pre><code class="lang-shell">nghttp --null-out --stat https://nginx-handbook.farhan.dev/index.html

# id  responseEnd requestStart  process code size request path
#  13     +1.49ms       +254us   1.23ms  200  492 /index.html
#   2     +1.56ms *    +1.35ms    212us  200  45K /mini.min.css
#   4     +1.71ms *    +1.39ms    318us  200  18K /the-nginx-handbook.jpg
</code></pre>
<p>As you can see, although the assets were not requested, the server has sent them to the client. Looking at the time measurements, process time has gone down and the three responses ended almost simultaneously.</p>
<p>This was a very simple example of server push but depending on the necessities of your project, this configuration can become much complex. This <a target="_blank" href="https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/">article</a> by <a target="_blank" href="https://www.nginx.com/people/owen-garrett/">Owen Garrett</a> on the official NGINX blog can help you with more complex server push configuration.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I would like to thank you from the bottom of my heart for the time you've spent on reading this article. I hope you've enjoyed your time and have learned all the essentials of NGINX.</p>
<p>Apart from this one, I've written full-length handbooks on other complicated topics available for free on <a target="_blank" href="https://www.freecodecamp.org/news/author/farhanhasin/">freeCodeCamp</a>.</p>
<p>These handbooks are part of my mission to simplify hard to understand technologies for everyone. Each of these handbooks takes a lot of time and effort to write.</p>
<p>If you've enjoyed my writing and want to keep me motivated, consider leaving starts on <a target="_blank" href="https://github.com/fhsinchy/">GitHub</a> and endorse me for relevant skills on <a target="_blank" href="https://www.linkedin.com/in/farhanhasin/">LinkedIn</a>. I also accept sponsorship so you may consider <a target="_blank" href="https://www.buymeacoffee.com/farhanhasin">buying me a coffee</a> if you want to.</p>
<p>I'm always open to suggestions and discussions on <a target="_blank" href="https://twitter.com/frhnhsin">Twitter</a> or <a target="_blank" href="https://www.linkedin.com/in/farhanhasin/">LinkedIn</a>. Hit me with direct messages.</p>
<p>In the end, consider sharing the resources with others, because </p>
<blockquote>
<p>Sharing knowledge is the most fundamental act of friendship. Because it is a way you can give something without loosing something. — Richard Stallman</p>
</blockquote>
<p>Till the next one, stay safe and keep learning.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Server Definition ]]>
                </title>
                <description>
                    <![CDATA[ In computing, a server is a computer or computers that send data over a network. This data can be a website, a program or photo file, or even information about the weather for an app on your phone. Servers listen for requests from clients, which are ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/server-definition/</link>
                <guid isPermaLink="false">66c35e8fc337fbd10a4b595f</guid>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tech Terms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 18 Mar 2021 06:23:00 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/605c2c33687d62084bf6cbcd.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In computing, a server is a computer or computers that send data over a network. This data can be a website, a program or photo file, or even information about the weather for an app on your phone.</p>
<p>Servers listen for requests from clients, which are other computers or devices. When a server hears a request, it reads it and responds by sending data back to the client.</p>
<p>For example, when you enter <code>freecodecamp.org</code> in the address bar, our server hears the request from your browser. Our server reads what page you want and looks for all the data (HTML, CSS, JavaScript, and photos) for that page. Then when everything is ready, it sends that data back to your browser.</p>
<p>Almost any computer with the correct software can act as a server. But the servers behind many popular websites and services are often built with special hardware. This allows the servers to handle many requests at once, and stay online for long periods of time.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Server Security Tips – Secure Your Server with These Best Practices ]]>
                </title>
                <description>
                    <![CDATA[ By Riya Sander Servers play a vital role in organizations. Their primary function is to provide both data and computational services. Because of the critical role they play, servers hold confidential organizational data and information. Information i... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/server-security-tips/</link>
                <guid isPermaLink="false">66d460c7b3016bf139028d89</guid>
                
                    <category>
                        <![CDATA[ authentication ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ information security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 08 Mar 2021 16:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/03/Server-Security-Tips-to-Secure-Your-Server-4.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Riya Sander</p>
<p>Servers play a vital role in organizations. Their primary function is to provide both data and computational services.</p>
<p>Because of the critical role they play, servers hold confidential organizational data and information. Information is like gold nowadays, and hackers are gold miners.</p>
<p>An insecure server is vulnerable to all sorts of security threats and data breaches.</p>
<p>Security vulnerabilities can lead to the loss of critical data or loss of capability and control that can jeopardize the whole organization.</p>
<p>If you do not secure your servers, then you are treading a dangerous path.</p>
<p>You might not know how to secure your servers properly. This article will explain some of the server security tips that you can use to secure your servers.</p>
<h2 id="heading-server-security-best-practices"><strong>Server Security Best Practices</strong></h2>
<h3 id="heading-1-constantly-upgrade-the-software-and-the-operating-system">1. Constantly Upgrade the Software and the Operating System</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Constantly-Upgrade-the-Software-and-the-Operating-System.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Constantly Upgrade the Software and the Operating System</em></p>
<p>In server security, staying up to date on all software and operating system-related security fixes is essential. Server systems and software technologies are so complicated that some of the security vulnerabilities they carry can easily go unnoticed.</p>
<p>Because of this, security vulnerabilities commonly exist in both old and freshly updated software versions. Also, hackers always try to develop new and innovative ways to gain unauthorized entries into a system.</p>
<p>Fortunately, vendors and cybersecurity experts are constantly working to ensure that their software and operating systems are as secure as possible. Once they discover a security loophole, they will typically move quickly to have the loophole fixed.</p>
<p>Once that's done, they will release a more secure and upgraded version of their operating system or software. For your server's security, you should immediately install the update once the vendor has tested and released it on the market.</p>
<p>Even though most vendors act speedily to address security vulnerabilities, there is always a gap between the time the security vulnerability is discovered, the time it takes to fix it, and the time it takes you to install the new update.</p>
<p>This gap can give hackers an upper hand since they can easily breach your servers before you make the update.</p>
<p>To keep this gap as small as possible, always remain vigilant and aware of any new developments as far as your servers' security is concerned. You should also be mindful of the immediate measures you can take to ensure that you are not affected by the vulnerable software.</p>
<p>For instance, uninstalling the software could be an essential thing to do. Lastly, you should install the new update right away once it has been released. Installing the secure operating system and software version can help reduce your vulnerability.</p>
<h3 id="heading-2-configure-your-computer-to-file-backups">2. Configure Your Computer to File Backups</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Configure-Your-Computer-to-File-Backups.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Configure Your Computer to File Backups</em></p>
<p>You should always keep a file backup and have a restoration strategy. You never know when a hacker will succeed in breaching your servers.</p>
<p>When such a breach happens, a backup file could be your savior.</p>
<p>Regularly backing up your data allows you to restore all the information resources that your server held before the data breach took place.</p>
<p>Therefore, for the sake of your data, you should ensure that you <a target="_blank" href="https://www.freecodecamp.org/news/it-security-and-data-backups/">regularly undertake the data backup</a>.</p>
<p>When developing a backup plan, make sure that you do a thorough analysis of the following:</p>
<ul>
<li>the cost of the backup plan, </li>
<li>its efficiency and speed, </li>
<li>the effort required to restore your data after a data breach, </li>
<li>the speed of the backup process, and </li>
<li>the amount of disc space that you need to store the data. </li>
</ul>
<p>You also should carefully consider the location where you store your backup files.</p>
<p>You can choose to keep the files either locally or on on the cloud, which is a safer approach.</p>
<h3 id="heading-3-set-up-access-limitations-to-your-computers-files">3. Set up Access Limitations to Your Computers files</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Access-Limitations-to-Your-Computers-files.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Access Limitations to Your Computers files</em></p>
<p>Most operating systems will give users the option to specify access privileges. For the safety of your servers, I advise that you be as restrictive as possible.</p>
<p>A user can specify access privileges to directories, networks, files, and other server elements. Access controls can reduce both deliberate and unintended server security breaches.</p>
<p>For instance, limiting read access can help you protect confidential and private information. Similarly, restricting who can modify files and data will help maintain the integrity of the files.</p>
<p>Not all employees should be given access to all the resources of your organization. Applying the principle of the least privilege is an excellent move in securing your servers.</p>
<p>Those who have no business with server resources or do not need them to fulfill their job requirements should not have access to those resources. Some of the worst data breaches have been organized and executed by people within the organization who had access to crucial data and information.</p>
<p>A <a target="_blank" href="https://enterprise.verizon.com/resources/executivebriefs/2019/insider-threat-report-executive-summary.pdf">2019 Insider Threat Report</a> by Verizon places careless and malicious workers as the top actors in insider cybersecurity threats. Limiting access, therefore, can help protect your servers from insider attacks.</p>
<h3 id="heading-4-install-ssl-certificates">4. Install SSL Certificates</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Install-SSL-Certificates.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Install SSL Certificates</em></p>
<p>Secure Socket Layer certificates are security protocols that guard the communication between two systems over the internet.</p>
<p>The Secure Socket Layer is a crucial element of server security. You need to ensure that any communication or data transfers between your server and clients' browsers or other servers are encrypted.</p>
<p>SSL certificates scramble data in transit so that sensitive and confidential information such as health details, credit card details, and financial records remain secure. A hacker who succeeds in accessing the data cannot decipher its meaning.</p>
<p>Only the intended recipient who has the right key to decrypt the information will understand its meaning.</p>
<p>Apart from just encrypting the communication between your servers and other parties, SSL certificates also play a critical role in user authentication.</p>
<p>SSL certificates can authenticate different systems to their particular owners. The certificate, therefore, helps establish your authority. To strengthen your security, you should get and install an SSL certificate.</p>
<h3 id="heading-5-use-virtual-private-networks-private-networking">5. Use Virtual Private Networks (Private Networking)</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Use-Virtual-Private-Networks--Private-Networking-.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Use Virtual Private Networks (Private Networking)</em></p>
<p>Private networks are based on Internet Protocol address space. A VPN is said to be private because no Internet Protocol packets addressed are transmitted via a public network.</p>
<p>A VPN will allow you to create a connection between different computer devices located in different places. It lets you to carry out operations on your servers in a secure manner.</p>
<p>You can exchange information with other servers on the same account without compromises from outside. To ensure that your server is safe, you should set up a <a target="_blank" href="https://www.freecodecamp.org/news/what-does-a-vpn-do-and-how-does-it-work-a-guide-to-virtual-private-networks/">Virtual Private Network</a>.</p>
<h3 id="heading-6-server-password-security">6. Server Password Security</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Server-Password-Security.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Server Password Security</em></p>
<p>When it comes to server security, make sure you use password best practices. The first step is to develop clear password policies and rules that all members using the server should follow.</p>
<p>You should enforce minimum character length for passwords, set password complexity guidelines, enable session timeout for inactivity, and use a multiple-factor authentication strategy.</p>
<p>It's also useful to have a clear password expiration policy. Passwords should only be allowed to last a few weeks or months. It's best to encourage all users to <a target="_blank" href="https://www.freecodecamp.org/news/actually-secure-passwords/">implement safe password</a> storage to avoid passwords landing in unsafe hands.</p>
<h3 id="heading-7-use-firewall-protection">7. Use Firewall Protection</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Use-Firewall-Protection.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Use Firewall Protection</em></p>
<p>Firewalls are a must-have to ensure that your servers are safe. They filter incoming and outgoing traffic to allow only specific services and lockout unsafe ones.</p>
<p>There are a few different classifications of firewalls. The first group deals with the public services that anyone on the internet anonymously accesses. The second is the private services that a select group of authorized accounts can access. The last is the internal services that require no exposure to the outside world.</p>
<p>You should restrict access to these services depending on which group fits the situation. You should configure your server to refuse all accesses except those that are mandatory to your servers.</p>
<h2 id="heading-summing-up">Summing up</h2>
<p>After reading this article and implementing the server security measures I explained, you should feel more confident about your server's security.</p>
<p>As a best practice, you should implement these security measures when you first set your server up. It also helps if you implemented more than one of these measures. As a general rule, the more security measures you have, the safer your server will become.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Deploy a Node.js App – From Server Setup to Production ]]>
                </title>
                <description>
                    <![CDATA[ By Yiğit Kemal Erinç In this tutorial, we are going to learn everything we need to know before deploying a Node app to a production server. We will start by renting a server on Digital Ocean. Then we'll configure this server, connect to it, install N... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/deploy-nodejs-app-server-to-production/</link>
                <guid isPermaLink="false">66d45e41182810487e0ce14d</guid>
                
                    <category>
                        <![CDATA[ deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ nginx ]]>
                    </category>
                
                    <category>
                        <![CDATA[ node ]]>
                    </category>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 01 Mar 2021 21:39:43 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/603a54d9a675540a22924662.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Yiğit Kemal Erinç</p>
<p>In this tutorial, we are going to learn everything we need to know before deploying a Node app to a production server.</p>
<p>We will start by renting a server on Digital Ocean. Then we'll configure this server, connect to it, install Nginx and configure it, pull or create our Node app, and run it as a process. </p>
<p>As you can see, there is a lot to do and it will be an action-packed tutorial. So let's get started without wasting any time.</p>
<p>You should have some basic knowledge on how the Terminal works and how to work in Vi/Vim before getting started. If you are not familiar with basic commands, I would advise you to read up on them a bit. </p>
<p>I will run the commands in MacOS. If you want to follow this tutorial in Windows, you can use Powershell or some other Unix emulator of your choice.</p>
<p>Although I will use Node.js as the platform of our example application, most of the steps are the same for any web application.</p>
<h2 id="heading-why-digital-ocean">Why Digital Ocean?</h2>
<p>I choose Digital Ocean because it is cheap and the interface is really easy to use, compared to the likes of AWS. Also, a $100 credit is included in the GitHub student pack so you do not have to pay anything for a couple of months. It is ideal for deploying a course or hobby project.</p>
<p>It has a concept called Droplets, which is basically your share of a server. You can think of the server as an apartment in which you own or rent a flat.</p>
<p>Droplets work with the help of Virtual Machines which run on the server. So a Droplet is your Virtual Machine on a shared server. Since it is a VM, its CPU and memory share can be easily increased, usually by throwing more money at your provider.</p>
<h2 id="heading-how-to-create-a-digital-ocean-project">How to Create a Digital Ocean Project</h2>
<p><img src="https://erinc.io/wp-content/uploads/2021/02/image-3.png" alt="This image has an empty alt attribute; its file name is image-3.png" width="600" height="400" loading="lazy"></p>
<p>I am assuming that you have already signed up and logged in to Digital Ocean before proceeding. We should first create a project that will contain our droplets. Let's click on the new project button on the left side menu. It will ask you to name your project.</p>
<p><img src="https://erinc.io/wp-content/uploads/2021/02/Screen-Shot-2021-02-22-at-13.35.06.png" alt="This image has an empty alt attribute; its file name is Screen-Shot-2021-02-22-at-13.35.06.png" width="600" height="400" loading="lazy"></p>
<p>Enter whatever name you want. It will also ask you if you want to move any resources, but for now just click Skip – we will create the droplet later.</p>
<h2 id="heading-how-to-create-a-droplet-on-digital-ocean">How to Create a Droplet on Digital Ocean</h2>
<p>Let's create our droplet by clicking the Get Started button.</p>
<p><img src="https://erinc.io/wp-content/uploads/2021/02/image-4-1024x593.png" alt="This image has an empty alt attribute; its file name is image-4-1024x593.png" width="600" height="400" loading="lazy"></p>
<p>After clicking the button, it will ask us to choose a VM image.</p>
<p><img src="https://erinc.io/wp-content/uploads/2021/02/Screen-Shot-2021-02-22-at-13.12.43-1024x567.png" alt="This image has an empty alt attribute; its file name is Screen-Shot-2021-02-22-at-13.12.43-1024x567.png" width="600" height="400" loading="lazy">
<em>Choosing an Image</em></p>
<p>On this page, I will select Ubuntu 20.04 since it is the latest LTS version at the time I am writing this post. LTS means "Long Term Support". It is best to go with the LTS version for actual projects, because the provider guarantees that it will be supported and maintained for a long time. This means you will not have problems in the long run.</p>
<p>I have chosen Ubuntu, and would recommend it to you since it is the most commonly used Linux distribution. This means it's also the easiest to find answers to your future questions.</p>
<p>You can also choose to have a Dedicated CPU if you need it. If you are building your own startup or any business project, I would recommend reading this <a target="_blank" href="https://www.digitalocean.com/docs/droplets/resources/choose-plan/">post</a> which contains detailed instructions about how to pick the right option for you.</p>
<p>I will go with the cheapest option in this case. </p>
<p>Then you will need to select a Datacenter region. You should pick the one that is closest to you to minimize network delay.</p>
<p><img src="https://erinc.io/wp-content/uploads/2021/02/image-2-1024x519.png" alt="This image has an empty alt attribute; its file name is image-2-1024x519.png" width="600" height="400" loading="lazy">
<em>Select a Datacenter</em></p>
<p>Next let's select SSH Keys as the Authentication Method, since it is much more secure than basic password authentication. </p>
<p><img src="https://erinc.io/wp-content/uploads/2021/02/image-5-1024x459.png" alt="This image has an empty alt attribute; its file name is image-5-1024x459.png" width="600" height="400" loading="lazy">
<em>Authentication Method</em></p>
<p>To connect to the server we need to generate a new SSH key on our own device and add it to Digital Ocean.</p>
<h2 id="heading-how-to-generate-an-ssh-key">How to Generate an SSH Key</h2>
<p>I will generate the key on my macOS device. If you are using Windows you can refer to <a target="_blank" href="https://phoenixnap.com/kb/generate-ssh-key-windows-10">this</a> article. Open your terminal and move into the ssh folder:</p>
<pre><code>cd ~/.ssh
</code></pre><p>Then create your SSH key:</p>
<pre><code>ssh-keygen
</code></pre><p>If your computer says that it does not know this command, you should install it via brew.</p>
<p><img src="https://erinc.io/wp-content/uploads/2021/02/image-7-1024x140.png" alt="This image has an empty alt attribute; its file name is image-7-1024x140.png" width="600" height="400" loading="lazy"></p>
<p>It will ask you to name the file and enter a passphrase. Do not enter a name, just press enter and go with the defaults. You should have these files generated. I have named mine digital-ocean-ssh in this screenshot, so do not get confused by that.</p>
<pre><code>❯ lsid_dsa      id_rsa      known_hosts
</code></pre><p>Our public key is the <code>id_dsa</code> and the <code>id_rsa</code> is our private key. If you forget which one is private, you can always print one of them to see.</p>
<h2 id="heading-how-to-add-your-ssh-key-to-digital-ocean">How to Add Your SSH Key to Digital Ocean</h2>
<p>Now we want to copy our public key and upload it to Digital Ocean so they will know which key to use in authentication.</p>
<p><img src="https://erinc.io/wp-content/uploads/2021/02/image-9-1024x149.png" alt="This image has an empty alt attribute; its file name is image-9-1024x149.png" width="600" height="400" loading="lazy"></p>
<p>Copy this whole key including the ssh-rsa part.</p>
<p>Click on "New SSH Key":</p>
<p><img src="https://erinc.io/wp-content/uploads/2021/02/image-10.png" alt="This image has an empty alt attribute; its file name is image-10.png" width="600" height="400" loading="lazy"></p>
<p>Paste the key in the textbox that appears after you click the button and you should see your SSH key.</p>
<p><img src="https://erinc.io/wp-content/uploads/2021/02/image-11.png" alt="This image has an empty alt attribute; its file name is image-11.png" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-connect-to-the-server">How to Connect to the Server</h2>
<p>We will use the terminal to connect to our server with SSH. You can also take a look at Termius for a nice interface if you want.</p>
<p>Run this command in your terminal after replacing the IP_ADDRESS with your server's IP address (you can look it up from Digital Ocean's panel).</p>
<pre><code>ssh root@IP_ADDRESS
</code></pre><p>If everything goes well, now you should be in the server's terminal. We have successfully connected to server. If there is any error, you can debug it by running the command with the "-v" option or "-vv" for even more verbosity.</p>
<h2 id="heading-how-to-set-up-the-server">How to Set Up the Server</h2>
<p>We need to do some initial setup before deploying the Node app to the server.</p>
<h3 id="heading-update-and-upgrade-software">Update and Upgrade Software</h3>
<p>We want to update the server's software to make sure we are using the latest versions. </p>
<p>Many servers are vulnerable to attacks because they are using older versions of software with known vulnerabilities. Attackers can search for the vulnerabilities in those software and try to exploit them in order to gain access to your server. </p>
<p>You can update Ubuntu's software using the <strong>"apt update"</strong> command.</p>
<pre><code>apt updateHit:<span class="hljs-number">1</span> https:<span class="hljs-comment">//repos.insights.digitalocean.com/apt/do-agent main InReleaseGet:2 http://mirrors.digitalocean.com/ubuntu focal InRelease [265 kB]      Hit:3 http://mirrors.digitalocean.com/ubuntu focal-updates InRelease                Get:4 http://security.ubuntu.com/ubuntu focal-security InRelease [109 kB]Hit:5 http://mirrors.digitalocean.com/ubuntu focal-backports InReleaseFetched 374 kB in 1s (662 kB/s)                          Reading package lists... DoneBuilding dependency tree       Reading state information... Done96 packages can be upgraded. Run 'apt list --upgradable' to see them.</span>
</code></pre><p>If you read the message, it says that "96 packages can be upgraded". We have installed the new software packages but we have not upgraded our software to those versions yet. </p>
<p>To do that, let's run another command:</p>
<pre><code>apt upgrade
</code></pre><p>Type y when it prompts you and it will upgrade the software.</p>
<h3 id="heading-create-a-user">Create a User</h3>
<p>We have connected to the server as the root user (the user with the highest privileges). Being the root is dangerous and can open us up to vulnerabilities. </p>
<p>Therefore we should create a new user and not run commands as root. Replace <code>$username</code> with a username of your choice.</p>
<pre><code>whoamiroot
</code></pre><pre><code>adduser $username
</code></pre><p>You need to enter a password for the user. After that point, it will ask a bunch of questions, so just input y until the prompting is over.</p>
<p>The new user has been created but we also need to add this new user to the "sudo" group so that we can perform any action we need.</p>
<pre><code>usermod -aG sudo $USERNAME
</code></pre><p>We add group with the <code>-aG</code> (add group) option, and we add the group name <code>sudo</code> to our username.</p>
<p>We are still root, so let's switch our user to the newly created user, using the <code>su</code> (switch user) command.</p>
<pre><code>su $USERNAME
</code></pre><p>After this point, if you run <strong><code>whoami</code></strong> command, you should see your username. You can confirm the existence of the sudo group by running this command:</p>
<pre><code>sudo cat /<span class="hljs-keyword">var</span>/log/auth.log
</code></pre><p>Only superusers can view this file and OS will ask for your user password after you run this command.</p>
<h3 id="heading-copy-the-ssh-key">Copy the SSH Key</h3>
<p>We have successfully created the user but we have not enabled SSH login for this new user yet. </p>
<p>Therefore, we have to copy the public key that we previously created on our local computer and paste it into this user's SSH folder so SSH can know which key should it use to authenticate our new user.</p>
<pre><code>mkdir -p ~/.ssh
</code></pre><p>The <code>-p</code> argument creates the directory if it does not exist.</p>
<pre><code>vi ~<span class="hljs-regexp">/.ssh/</span>authorized_keys
</code></pre><p>We will use vi or vim to create a file and call it <strong><code>authorized_keys</code></strong>.</p>
<p>Copy your public key (<code>id_dsa</code> file) then press "i" to go into insert mode. Then just paste it into this file with CMD + V.</p>
<p>Press esc to quit insert mode, type <strong>:wq</strong> to save and quit.</p>
<p>If you have any problems about using Vim-Vi, you can check out <a target="_blank" href="https://www.freecodecamp.org/news/how-not-to-be-afraid-of-vim-anymore-ec0b7264b0ae/">one of the many tutorials</a> that explain how to use it.</p>
<h3 id="heading-connect-to-server-as-new-user">Connect to Server as New User</h3>
<p>Now we should be able to connect to the server without any problems using ssh. You can use this command to connect, just remember to insert your username and <code>IP_ADDRESS</code>.</p>
<pre><code>ssh $USERNAME@IP_ADDRESS
</code></pre><p>If you are having any problems at this point, you should just delete the droplet and start over. It does not take a lot of time to start over but debugging server problems can be difficult.</p>
<h3 id="heading-how-to-disable-root-login">How to Disable Root Login</h3>
<p>It is a good practice to disable Root login as a security precaution, so let's do that now.</p>
<p>It can be useful to change the file permission just in case so that we won't run into problems regarding permissions in the future.</p>
<pre><code>chmod <span class="hljs-number">644</span> ~<span class="hljs-regexp">/.ssh/</span>authorized_keys
</code></pre><p>Let's now open our <code>sshd_config</code> file:</p>
<pre><code>sudo vi /etc/ssh/sshd_config
</code></pre><p>Find this line and change the yes to no in the same way we did earlier with vi.</p>
<pre><code>PermitRootLogin no
</code></pre><p>Save and quit vi.</p>
<h2 id="heading-how-to-install-nodejs-and-git">How to Install Node.js and Git</h2>
<p>We can now go ahead and install Node.js and Git:</p>
<pre><code>sudo apt install nodejs npm
</code></pre><pre><code>sudo apt install git
</code></pre><p>We are now ready to create a Node app and run it. You can either pull your Node project from Github or create a Node app here just to test if it works.</p>
<p>Move to a directory of your choice and create an <strong>"app.js"</strong> file:</p>
<pre><code>sudo vi app.js
</code></pre><p>You can paste the following snippet into your <strong>app.js</strong> file:</p>
<pre><code><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);<span class="hljs-keyword">const</span> app = express();<span class="hljs-keyword">const</span> port = <span class="hljs-number">3000</span>;app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {        res.send(<span class="hljs-string">'Hello World'</span>);});app.listen(port, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Example app listening on port <span class="hljs-subst">${port}</span>!`</span>));
</code></pre><p>Now we can run it with the command:</p>
<pre><code>node app.js
</code></pre><p>You should see "Example app listening on port 3000!" on your terminal.</p>
<p>We can confirm that it is working by sending a request to our server:</p>
<pre><code>GET http:<span class="hljs-comment">//IP_ADDRESS:3000/</span>
</code></pre><p>Send this request either from an HTTP client like Postman or your browser and you should see the "Hello World" message.</p>
<p>At this point, you should notice that something is wrong: Regular users do not know how to send requests to port 3000.</p>
<p>We should redirect the requests that come to our web server from our IP to port 3000. We can accomplish this with the help of Nginx.</p>
<p><img src="https://erinc.io/wp-content/uploads/2021/02/image-16.png" alt="This image has an empty alt attribute; its file name is image-16.png" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-install-and-configure-nginx">How to Install and Configure Nginx</h2>
<p>We will use Nginx as a Reverse Proxy to redirect the requests to our Node app.</p>
<p><img src="https://erinc.io/wp-content/uploads/2021/02/image-14-1024x531.png" alt="This image has an empty alt attribute; its file name is image-14-1024x531.png" width="600" height="400" loading="lazy">
<em>Nginx as a Reverse Proxy</em></p>
<p>Let's install Nginx:</p>
<pre><code>sudo apt install nginx
</code></pre><p>Start the Nginx service:</p>
<pre><code>sudo service nginx start
</code></pre><p>We can test to see if it is working by sending a request to our server's IP address from the browser. Type your server's IP address to your browser and you should see this:</p>
<p><img src="https://erinc.io/wp-content/uploads/2021/02/image-15-1024x231.png" alt="This image has an empty alt attribute; its file name is image-15-1024x231.png" width="600" height="400" loading="lazy"></p>
<p>It is important to know that Nginx serves from <strong>"/var/www/html"</strong> by default and you can find this HTML file in that directory as well.</p>
<p>I also advise you to create a folder under "/var/www", call it app, and move your Node app to that folder so it will be easy to find.</p>
<h3 id="heading-how-to-configure-the-nginx-reverse-proxy">How to Configure the Nginx Reverse Proxy</h3>
<p>We will edit the Nginx config file to configure a reverse proxy:</p>
<pre><code>sudo vi /etc/nginx/sites-available/<span class="hljs-keyword">default</span>
</code></pre><p>In this file you need to find the location / block and change it as follows:</p>
<pre><code>location / {                # First attempt to serve request <span class="hljs-keyword">as</span> file, then                # <span class="hljs-keyword">as</span> directory, then fall back to displaying a <span class="hljs-number">404.</span>                proxy_pass http:<span class="hljs-comment">//127.0.0.1:3000/;        }</span>
</code></pre><p>The <code>proxy_pass</code> directive proxies the request to a specified port. We give the port that our Node application is running on.</p>
<p>Let's restart Nginx so the changes can take effect:</p>
<pre><code>sudo service nginx reload
</code></pre><p>After this step, we should be able to see the message when we send a request to our server. Congratulations, we have completed the minimum number of steps to deploy a Node app! </p>
<p><img src="https://erinc.io/wp-content/uploads/2021/02/Screen-Shot-2021-02-24-at-01.10.33-1024x67.png" alt="This image has an empty alt attribute; its file name is Screen-Shot-2021-02-24-at-01.10.33-1024x67.png" width="600" height="400" loading="lazy"></p>
<p>But I still advise you to complete the following bonus step as well, as I believe it's quite important.</p>
<p>If you can't see the hello world message, you can check if your app and Nginx are running and restart them.</p>
<h2 id="heading-how-to-run-your-app-as-a-process">How to Run your App as a Process</h2>
<p>We do not want to start our application manually every time something goes wrong and our app crashes. We want it to restart on its own. Also, whenever the server starts, our app should start too. </p>
<p>To make this happen, we can use PM2. Let's install PM2 and configure it.</p>
<pre><code>sudo npm i -g pm2
</code></pre><p>We are installing pm2 globally by using the "-g" option so that it will be accessible from every folder.</p>
<pre><code>pm2 start app.js
</code></pre><p>This makes sure that the app will restart if it exits due to an error.</p>
<p>Let's save the current process list.</p>
<pre><code>pm2 save
</code></pre><p>We also need to convert it to a daemon that runs whenever the system starts:</p>
<pre><code>pm2 startup systemd
</code></pre><p><img src="https://erinc.io/wp-content/uploads/2021/02/image-17.png" alt="This image has an empty alt attribute; its file name is image-17.png" width="600" height="400" loading="lazy"></p>
<p>As a reminder, in this tutorial, I'm using the commands for Ubuntu. If you are using any other Linux distro, you should replace <code>systemd</code> in this command.</p>
<p>We can confirm that the service is getting restarted by rebooting the server and sending a request without running app.js by hand:</p>
<pre><code>sudo reboot
</code></pre><p>After sending a request as we did earlier, you should be able to see the hello world message.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial we started from scratch, rented a server for ourselves, connected to it, and configured it in a way that it serves our Node.js app from port 80. </p>
<p>If you have followed along and were able to complete all steps, congratulations! You can be proud of yourself, as this was not the easiest topic :). I hope that you have learned a lot. Thank you for your time.</p>
<p>I am planning to explore this topic further by connecting the server to a domain name, then connecting it to CircleCI for continuous integration. I'll also go through the required steps to make your Node.js/React app production ready. This post had already gotten long enough, though, so those topics are reserved for another post :)</p>
<p>If you have enjoyed reading and want to get informed about my future posts, you can subscribe to my <a target="_blank" href="https://erinc.io/">personal blog</a>. You can see my previous posts there if you are interested in reading more. I usually write about web development-related topics.  </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is the Correct Content-Type for JSON? Request Header Mime Type Explained ]]>
                </title>
                <description>
                    <![CDATA[ By Dillion Megida Every resource used on the internet has a media type, also known as a MIME type which stands for Multipurpose Internet Mail Extension. This information is necessary for transactions between server and client. The browser needs to kn... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-the-correct-content-type-for-json-request-header-mime-type-explained/</link>
                <guid isPermaLink="false">66d84f715a0b456f4d321469</guid>
                
                    <category>
                        <![CDATA[ browser ]]>
                    </category>
                
                    <category>
                        <![CDATA[ internet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ json ]]>
                    </category>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 08 Dec 2020 22:07:17 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5fcf4739e6787e098393bd6d.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Dillion Megida</p>
<p>Every resource used on the internet has a media type, also known as a MIME type which stands for Multipurpose Internet Mail Extension. This information is necessary for transactions between server and client.</p>
<p>The browser needs to know the media type of resources sent to it so that it can handle them properly.</p>
<p>The same goes for the server. It needs to know the type of resources sent to it for accurate parsing and processing.</p>
<h2 id="heading-where-is-the-content-type-declared">Where is the Content-Type declared?</h2>
<p>The media type of any resource is declared in the <code>Content-Type</code> property of the request header (on the client, when making a request to the server) or in the response header (on the server, when sending a response).</p>
<p>Without explicitly declaring the content type of a resource, the client may attempt to automatically detect the type, but the result may not be accurate. This is why it's important to explicitly declare it.</p>
<h2 id="heading-media-types">Media Types</h2>
<p>Media types exist in various forms. They are categorized into various groups:</p>
<ul>
<li>application</li>
<li>audio</li>
<li>font</li>
<li>example</li>
<li>image</li>
<li>message</li>
<li>model</li>
<li>multipart</li>
<li>text</li>
<li>and video</li>
</ul>
<p>These categories also have their types. For example, <code>application/json</code> is a type under <code>application</code> and <code>text/html</code> is a type under <code>text</code>.</p>
<p>You can find a complete list of media types in the <a target="_blank" href="https://www.iana.org/assignments/media-types/media-types.xhtml">IANA</a> (a body that coordinates some of the key elements on the internet) media types.</p>
<p>All these types cover various data types like text, audio, images, HTML, and many more types that are used across the internet.</p>
<h2 id="heading-the-browser-needs-to-know-the-media-type-of-a-resource">The browser needs to know the media type of a resource</h2>
<p>As I mentioned above, the browser needs to know what type of content it receives. Here's an example to illustrate that.</p>
<p>The following code is a Node server that serves an HTML file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">"http"</span>);
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);
<span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">"path"</span>);

<span class="hljs-keyword">const</span> server = http.createServer(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">req, res</span>) </span>{
    <span class="hljs-keyword">const</span> filePath = path.join(__dirname, <span class="hljs-string">"index.html"</span>);
    <span class="hljs-keyword">var</span> stat = fs.statSync(filePath);

    res.writeHead(<span class="hljs-number">200</span>, {
        <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"text/css"</span>,
        <span class="hljs-string">"Content-Length"</span>: stat.size,
    });

    <span class="hljs-keyword">const</span> readStream = fs.createReadStream(filePath);
    readStream.pipe(res);
});

server.listen(<span class="hljs-number">5000</span>);

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Node.js web server at port 5000 is running.."</span>);
</code></pre>
<p>Do not worry about the specifics of the code. All you're concerned with is the <code>index.htm</code> file we're serving and that the <code>Content-Type</code> is <code>text/css</code>.</p>
<p>Here's the content of <code>index.html</code>:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Homepage<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</code></pre>
<p>Of course, an HTML document is different from a  CSS file. Here's the result on <code>localhost:5000</code> when the server is started:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/Screenshot-2020-12-08-at-10.12.32.png" alt="Screenshot-2020-12-08-at-10.12.32" width="600" height="400" loading="lazy"></p>
<p>You can also confirm the response gotten by checking the headers in the network tab of the DevTools.</p>
<p>Here's the result on a Chrome browser:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/Screenshot-2020-12-08-at-10.13.34.png" alt="Screenshot-2020-12-08-at-10.13.34" width="600" height="400" loading="lazy"></p>
<p>The browser got the content as a CSS type, therefore, it tried treating it as CSS.</p>
<p>Also, note that full knowledge of the type of content gotten by the browser also reduces security vulnerabilities as the browser knows security standards to put in place for that data.</p>
<p>Now that you understand the concept of MIME types and their importance, let's head over to JSON.</p>
<h2 id="heading-the-correct-content-type-for-json">The Correct Content-Type for JSON</h2>
<p>JSON has to be correctly interpreted by the browser to be used appropriately. <code>text/plain</code> was typically used for JSON, but according to <a target="_blank" href="https://www.iana.org/assignments/media-types/media-types.xhtml">IANA</a>, the official MIME type for JSON is <code>application/json</code>.</p>
<p>This means when you're sending JSON to the server or receiving JSON from the server, you should always declare the <code>Content-Type</code> of the header as <code>application/json</code> as this is the standard that the client and server understand.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>As stated above, the server (just like the browser) needs to know the type of data sent to it, say, in a POST request. That's the reason <code>forms</code> with files usually contain the <code>enctype</code> attribute with a value of <code>multipart/form-data</code>. </p>
<p>Without encoding the request this way, the POST request won't work. Also, once the server knows the type of data it has gotten, it then knows how to parse the encoded data.</p>
<p>In this article, we looked at what MIME types are and their purpose. Also, we looked at the official content type for JSON. I hope you now know why it's important to declare resource types when used across the internet.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ HTTP Error 503 Service Unavailable Explained – What the 503 Error Code Means ]]>
                </title>
                <description>
                    <![CDATA[ Errors happen – there's some unexpected maintenance, a bug that went unnoticed, or a page goes viral and the flood of connections take the server down. If you've been online for any amount of time, no doubt you've seen the somewhat vague 503 Service ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/http-error-503-service-unavailable-explained-what-the-503-error-code-means/</link>
                <guid isPermaLink="false">66ac881733a54a9b1a447932</guid>
                
                    <category>
                        <![CDATA[ error ]]>
                    </category>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ web ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kristofer Koishigawa ]]>
                </dc:creator>
                <pubDate>Fri, 02 Oct 2020 02:36:00 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c986c740569d1a4ca19fd.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Errors happen – there's some unexpected maintenance, a bug that went unnoticed, or a page goes viral and the flood of connections take the server down.</p>
<p>If you've been online for any amount of time, no doubt you've seen the somewhat vague 503 Service Unavailable error.</p>
<p>In this article we'll go over HTTP status codes, what the 503 error means, and some possible ways to solve it – both for a site you're trying to visit and for your own site.</p>
<h2 id="heading-an-overview-of-http-status-codes">An overview of HTTP status codes</h2>
<p>Servers that host web pages listen for requests from web browsers or devices, also known as clients. The server then uses a bunch of different status codes to communicate back.</p>
<p>These status codes are organized into different classes, which is indicated by the first number of the status code:</p>
<ul>
<li>1xx: Information – the server is still processing the request</li>
<li>2xx: Success – the request succeeded and the server responds with the page or resource</li>
<li>3xx: Redirection – the page or resource has moved and server will respond with its new location</li>
<li>4xx: Client error – there is an error in the request from the browser or device</li>
<li>5xx: Server error – there is an error with the server</li>
</ul>
<p>The last two digits of each HTTP status code represent a more specific status for each class. For example, 301 means that a page or resource has moved permanently, while 302 means the move is temporary.</p>
<p>Check out this page for a list of common HTTP status codes and their meaning: <a target="_blank" href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes">https://en.wikipedia.org/wiki/List_of_HTTP_status_codes</a></p>
<p>Most status codes go by totally unnoticed, which is fine because it means everything is working. It's only when you get to the 4xx-5xx range that you might notice a status code because you'll see a page like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/E20Ry-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>A typical 503 error page – Source: <a target="_blank" href="https://stackoverflow.com/questions/27944151/asp-net-website-shows-503-service-unavailable-after-successful-publishing">Stack Overflow</a></em></p>
<p>Now that you have a basic understanding of HTTP status codes, let's dig a bit deeper into the 503 Service Unavailable error.</p>
<h2 id="heading-what-does-the-503-error-code-mean">What does the 503 error code mean?</h2>
<p>As mentioned above, 5xx status codes mean there's a problem with the server itself.</p>
<p>A 503 Service Unavailable error means that the page or resource is unavailable. There are many reasons why a server might return a 503 error, but some common reasons are maintenance, a bug in the server's code, or a sudden spike in traffic that causes the server to become overwhelmed.</p>
<p>The message that's sent with the 503 error can vary depending on server it's coming from, but here are some of the common ones you'll see:</p>
<blockquote>
<ul>
<li>503 Service Unavailable  </li>
<li>503 Service Temporarily Unavailable  </li>
<li>HTTP Server Error 503  </li>
<li>HTTP Error 503  </li>
<li>Error 503 Service Unavailable  </li>
<li>The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.  </li>
</ul>
<p><a target="_blank" href="https://kinsta.com/blog/http-error-503/">Source</a></p>
</blockquote>
<p>Whatever the reason for the 503 error, it's usually temporary – the server will restart, traffic will die down, and the issue will resolve itself.</p>
<h2 id="heading-how-to-solve-the-503-status-unavailable-error">How to solve the 503 Status Unavailable error</h2>
<p>When trying to solve a 503 error, there are two general camps.</p>
<p>The first is where you're an end user, and you're trying to visit a site that you don't own. In the second, you own the site, and it's throwing 503 errors to people who are trying to visit.</p>
<p>The method to solve 503 errors is different depending on which group you fall into. Let's take a look at some things you can do as an end user if you see a 503 error.</p>
<h3 id="heading-how-to-solve-a-503-status-unavailable-error-as-an-end-user">How to solve a 503 Status Unavailable error as an end user</h3>
<p>Since 5xx status codes mean that the error is on the server-side, there isn't a lot you can do directly.</p>
<p>Even though 503 errors are usually temporary, there are some things you can do while you wait.</p>
<p><strong>#1: Refresh the page</strong></p>
<p>Sometimes the error is so temporary that a simple refresh is all it takes. With the page open, just press Ctrl - R on Windows and Linux, or Cmd - R on macOS to refresh the page.</p>
<p><strong>#2: See if the page is down for other people</strong></p>
<p>The next thing you can do is use a service like <a target="_blank" href="https://www.isitdownrightnow.com/">Is It Down Right Now?</a> or <a target="_blank" href="https://downforeveryoneorjustme.com/">Down For Everyone Or Just Me</a> to see if other people are getting the same error.</p>
<p>Just go to either of those sites and enter in the URL for the page you're trying to visit.</p>
<p>The service will ping the URL you entered to see if it gets a response. Then it'll show you some cool stats and graphs about the page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/image-44.png" alt="Image" width="600" height="400" loading="lazy">
<em>Checking <a target="_blank" href="https://www.freecodecamp.org/">freeCodeCamp</a> on Is It Down Right Now?</em></p>
<p>If you scroll down a bit you'll see some comments from other people. Often people will give their general location and other data, so this can be a good way to determine if the error is just affecting certain regions or specific devices.</p>
<p><strong>#3: Restart your router</strong></p>
<p>Sometimes the issue has to do with a DNS server failure.</p>
<p>DNS stands for Domain Name System, and they basically act as translators between IP addresses and human readable URLs.</p>
<p>For example, you can visit Google by entering its long IP address directly (172.217.25.206), or you can just enter in the URL, www.google.com.</p>
<p>It's a DNS, often hosted on a server, that handles all that behind the scenes.</p>
<p>All of that is to say, many routers cache responses from DNS servers (www.google.com &lt;==&gt; 172.217.25.206). But sometimes this cache can get corrupted and cause errors.</p>
<p>An easy way to reset or "flush" the cache is to restart your router. Just unplug your router for about 5 seconds, then plug it back in again.</p>
<p>It should restart after a minute and all of your devices should reconnect automatically. Once they do, try visiting the site again.</p>
<h3 id="heading-how-to-solve-a-503-status-unavailable-error-as-the-sites-owner">How to solve a 503 Status Unavailable error as the site's owner</h3>
<p>If you are the owner/developer of the site that's returning 503 errors, there's a bit more you can do to diagnose and resolve the issue.</p>
<p>Here are some general tips to get you started:</p>
<p><strong>#1: Restart the server</strong></p>
<p>Development is tough – even a simple static page can have so many moving parts that it can be difficult to pin down what's causing the 503 error.</p>
<p>Sometimes the best thing to do is to restart the server and see if that fixes the issue.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/1rs7t0-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Source: <a target="_blank" href="https://imgflip.com/i/1rs7t0">imgflip</a></em></p>
<p>The exact method of restarting your server can vary, but usually you can access it from your provider's dashboard or by SSH'ing into the server and running a restart command.</p>
<p>The server should restart after a couple of minutes. If you've configured everything to run automatically on boot, you can visit your site and see if it's working.</p>
<p><strong>#2: Check the server logs</strong></p>
<p>The next thing to do is check the logs.</p>
<p>The location of the server logs can vary depending on what service you're running, but they're often found in <code>/var/log/...</code>.</p>
<p>Take a look around that directory and see if you can find anything. If not, check the manual for your programs by running <code>man program_name</code>.</p>
<p><strong>#3: Check if there's ongoing automated</strong> maintenance</p>
<p>Some service providers offer automated package updates and maintenance. Normally this is a good thing – they usually occur during downtime, and help make sure everything is up-to-date.</p>
<p>Occasionally 503 errors are due to these scheduled maintenance sessions. </p>
<p>For example, some hosting providers that specialize in WordPress hosting automatically update WP whenever there's a new release. WordPress automatically returns a 503 Service Unavailable error whenever it's being updated.</p>
<p>Check with your service providers to see if the 503 error is being caused by scheduled maintenance.</p>
<p><strong>#4: Check your server's firewall settings</strong></p>
<p>Sometimes 503 Service Unavailable errors are cause by a misconfigured firewall where connections can get through, but fail to get back out to the client.</p>
<p>Your firewall might also need special settings for a CDN, where multiple connections from a small handful of IP addresses might be misinterpreted as a DDoS attack.</p>
<p>The exact method of adjusting your firewall's settings depends on a lot of factors. Take a look at your pipeline and your service provider's dashboards to see where you can configure the firewall.</p>
<p><strong>#5: Check the code</strong></p>
<p>Bugs, like errors, happen. Try as you might, it's impossible to catch them all. Occasionally one might slip through and cause a 503 error.</p>
<p>If you've tried everything else and your site is still showing a 503 Service Unavailable error, the cause might be somewhere in the code.</p>
<p>Check any server-side code, and pay special attention to anything having to do with regular expressions – <a target="_blank" href="https://www.freecodecamp.org/news/freecodecamp-servers-update-october-2019/">a small regex bug</a> is what caused a huge spike in CPU usage, rolling outages, and about three days of panic for us at freeCodeCamp.</p>
<p>Hopefully you'll be able to track down the culprit, deploy a fix, and everything will be back to normal.</p>
<h2 id="heading-in-summary">In summary</h2>
<p>That should be everything you need to know about 503 Service Unavailable errors. While there's usually not much you can do when you see a 503 error, hopefully some of these steps will help the next time you encounter one.</p>
<p>Stay safe, and happy refreshing-until-it-works :)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is a Proxy Server? In English, Please. ]]>
                </title>
                <description>
                    <![CDATA[ By Milecia McGregor Have you ever been traveling and couldn't get the same shows you normally watch back home on Hulu? Or have you noticed that some websites are blocked or you can't access certain services while you're connected to different Wi-Fi n... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-a-proxy-server-in-english-please/</link>
                <guid isPermaLink="false">66d46050264384a65d5a95b2</guid>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ privacy ]]>
                    </category>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Security ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 01 May 2020 04:27:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/04/Proxies-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Milecia McGregor</p>
<p>Have you ever been traveling and couldn't get the same shows you normally watch back home on Hulu? Or have you noticed that some websites are blocked or you can't access certain services while you're connected to different Wi-Fi networks? That's likely due to a proxy being in place.</p>
<h2 id="heading-what-is-a-proxy-server">What is a proxy server?</h2>
<p>A proxy server, or just proxy for short, is like having another computer that your internet requests get sent to before going to the real website. It's a server that takes all of the information you've sent out, like a request to buy new shirts on H&amp;M, and routes it through a different IP address.</p>
<p>That's what makes a proxy so powerful. They can make all of your internet activity appear as if it's coming from a completely different location. </p>
<p>Companies use them for security and network performance purposes, individuals use them for privacy concerns, and there's also some cool functionality you can tap into when using proxy servers for your internet browsing that we'll talk about later.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/proxy-flow.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>A proxy can be physically located anywhere. You can set up a proxy on your home computer or you can deploy one to the cloud. The main thing that matters is that the proxy has the configurations you need for the functionality you want.</p>
<p>Just remember that a proxy acts like a fancy IP address filter. Similar to filters, there are different kinds of proxies and they all have their specific uses. </p>
<p>To get started, let's talk about the most common kind of proxy and how it works, the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling">forward proxy</a>.</p>
<h2 id="heading-how-a-proxy-works">How a proxy works</h2>
<p>When you hear or see people talking about proxies, they're most likely referring to forward proxies. These are the most common types of proxy because they easily handle what most people need. Forward proxies act as the middleman between your requests and the server you're trying to connect to.</p>
<p>The way a proxy works is first you make a request, for example you try to go GitHub. So you type in the URL and hit enter. With a proxy, instead of connecting you directly to GitHub with your computer's IP address, your request gets intercepted by the proxy.</p>
<p>Then the proxy takes your request, updates it, and sends it from its own IP address. This can completely remove your IP address and identifying information from the request to the GitHub server.</p>
<p>One of the ways that proxies handle changing your request is directly in the request headers it sends to the server. A proxy request can set headers like <em>Forwarded</em> and <em>Via</em> in the original request before it sends the message to the server you're trying to get information from.</p>
<p>Once the proxy has updated the information from your request, it will send your reformatted request to the GitHub server. That server now thinks your request has come from a different location and it will send the data you wanted back through that location.</p>
<p>Next the proxy takes the data from the GitHub server and does any checks it has been configured to do with that data. It could check for any malicious scripts or other security issues. Then it finally sends the data back to your computer and your page loads.</p>
<p>A proxy server isn't necessarily limited to one user at a time. There can be multiple people sending requests through the same proxy and they can all share the same benefits. There are plenty of reasons you might use a proxy, even if it's a shared one.</p>
<h2 id="heading-why-you-would-use-a-proxy">Why you would use a proxy</h2>
<p>Now that you know what a proxy is, it's good to know some of the common use cases for them.</p>
<ul>
<li>You can increase network security by encrypting requests<br>○ Prevent hackers from intercepting sensitive information<br>○ Block malware sites from your real network</li>
<li>You can lower the amount of network traffic by caching sites<br>○ Cache websites so that only one request to the site is made no matter how many users are on the proxy</li>
<li>You can control how people use the internet<br>○ Block specific domains<br>○ Monitor and log all web requests</li>
<li>You can get around blocks set up by companies and countries<br>○ Access content from a different country<br>○ Get around corporate firewalls</li>
</ul>
<p>This isn't a comprehensive list of everything you can do with proxies, but I also wanted to include some of the other benefits that don't quite fall under the typical categories.</p>
<ul>
<li>You always have cookies blocked</li>
<li>You always have ads blocked</li>
<li>You can access the deep web</li>
<li>It removes any search tailoring or tracking your previous searches</li>
<li>You can scrape data</li>
<li>You can do research on your competition</li>
</ul>
<h2 id="heading-different-types-of-proxies">Different types of Proxies</h2>
<p>There are many different types of proxies that will cover just about any configuration that you can think of. Here, I'll give you a quick overview of 14 different proxy types.</p>
<h3 id="heading-transparent-proxy">Transparent proxy</h3>
<p>Transparent proxies are the simplest kind of proxy. They pass all of your information along, but with the proxy's IP address. These proxies don't offer any kind of privacy protection.</p>
<p>They tell the server you're sending your request to that the request is coming through a proxy. This is enough to get you around simple IP bans. A common use for transparent proxies is setting up website filtering, like schools and companies do.</p>
<h3 id="heading-anonymous-proxy">Anonymous proxy</h3>
<p>Anonymous proxies are a commonly used type of proxy. They never pass your IP address to the website you are browsing although they will identify themselves as a proxy in the request. This helps keep your browsing activity private.</p>
<p>When you don't want targeted ads following you around the internet or you don't want your location attached to your request, these are some standard proxies to use. This is usually enough to get around most targeting activities, but there is still a chance that your information might be revealed.</p>
<h3 id="heading-high-anonymity-proxy">High anonymity proxy</h3>
<p>These proxies are the most secure type because they don't pass along your IP address and personal data and they don't identify themselves as a proxy when making requests. They also sporadically change the IP address they use for requests. That's what allows high anonymity proxies to give you the most privacy online.</p>
<p>The TOR browser uses this type of proxy. Since the IP address changes occasionally, that makes it extremely hard for servers to keep track of what traffic belongs to what client. If you don't want to be tracked, this is the best option.</p>
<h3 id="heading-distorting-proxy">Distorting proxy</h3>
<p>A distorting proxy works similarly to an anonymous proxy except it passes an IP that is purposely false. It identifies itself as a proxy and uses that false IP address in requests. This is great when you want to appear as if you were in a specific location.</p>
<p>This is useful when you want to get around specific content restrictions. It's like you get to choose the IP address you want the proxy to use.</p>
<h3 id="heading-residential-proxy">Residential proxy</h3>
<p>Residential proxies are proxies that use real IP addresses. That means they are the addresses of real computers. These are the best types of proxies to use because they look like regular clients to servers.</p>
<p>Any of the proxy types discussed so far can be a residential proxy. As long as the proxy's IP address is associated with a physical device, these types of proxies tend to be undetectable and they get around some of the geographic problems other proxy types have.</p>
<h3 id="heading-data-center-proxy">Data center proxy</h3>
<p>These are kind of the opposite of residential proxies. Data center proxies have computer generated IP addresses that aren't attached to a real device. It's like having a proxy in the cloud.</p>
<p>An advantage to this kind of proxy is their speed. Usually cloud service providers have incredible internet connections that give you speeds you couldn't get otherwise. Although they would all share similar IP addresses, one server could host hundreds of data center proxies.</p>
<h3 id="heading-public-proxy">Public proxy</h3>
<p>Of all the proxy types, these are the most insecure, unreliable proxies available. They can go down at any moment and many are set up by hackers to steal data. The only reason people still use them is because they are free.</p>
<p>While it isn't difficult to find lists of free public proxies, it is a challenge to find good ones. You never know who these proxies are hosted by and it's a huge gamble to send any of your sensitive information through one. Any number of users can be on a public proxy at any time and there's no one regulating who uses it.</p>
<h3 id="heading-private-proxy">Private proxy</h3>
<p>Private proxies have some ambiguity around what they are because they're defined by the provider offering the service. This could mean your proxy can only be used by one client at a time or that your proxy needs authentication before you can use it. These are like more reliable versions of public proxies.</p>
<p>A private proxy can be transparent or have high anonymity, similar to some of the others above like the residential or data center proxy. This proxy type has more to do with who can connect to it than how it handles your requests.</p>
<h3 id="heading-dedicated-proxy">Dedicated proxy</h3>
<p>A dedicated proxy is like a specific type of private proxy. It just means that the proxy can't be shared by multiple clients at the same time. So only one client can connect and send requests.</p>
<p>This helps prevent the IP address of the proxy from getting banned by different websites and services. It's one of the ways that a proxy provider can control who has access to the proxy to make sure that it isn't being abused.</p>
<h3 id="heading-shared-proxy">Shared proxy</h3>
<p>These are some of the cheapest proxies available and they work similar to shared servers. Clients pool together and split the cost of the proxy and they can all access it at the same time. Shared proxies have a more complex architecture because they handle a lot of requests at the same time.</p>
<p>Depending on how resources are allocated on the shared proxy, requests might be slower than over your own IP address. Because it's handling multiple requests from multiple users, the configurations of these types of proxies is more critical than the others.</p>
<h3 id="heading-rotating-proxy">Rotating proxy</h3>
<p>Rotating proxies work a little differently from the others. Every time a client connects to the proxy, a new IP address is created for it. So they never use the same IP address more than once.</p>
<p>Every time a client sends a request a new IP address is generated. This is how proxies like the TOR browser work to keep your anonymity. A rotating proxy provides a high level of security and privacy when combined with some of the other types.</p>
<h3 id="heading-ssl-proxy">SSL proxy</h3>
<p>These proxies follow the same protocol as HTTPS requests. The 'S' in HTTPS means SSL which means your web requests are secure between your client and the server you're trying to get to.</p>
<p>That means you get even more security because all of your requests through the proxy are encrypted. Most proxies should be using this by default, but there is still a chance you'll run into some that use HTTP.</p>
<h3 id="heading-reverse-proxy">Reverse proxy</h3>
<p>Reverse proxies are completely different from everything we've covered so far. A reverse proxy hides the IP address of a server you're trying to send a request to. When a server needs security and privacy from clients, that's when these types of proxies come in.</p>
<p>These proxies are great if you need to monitor access to a server for reasons like keeping clients from having unmonitored access to a database. It can also help lower traffic on the network by passing on cached information instead of making a query each time.</p>
<h2 id="heading-proxy-services">Proxy Services</h2>
<p>If you've done a quick search for proxy services, you'll know that there are a lot to choose from. Not all of them are created equally, so it's important that you know what features you want from your proxy service. </p>
<p>Most of these services offer combinations of the proxy types. For example, you'll be able to find residential, high anonymity, SSL proxies rolled into one service. There are a few that stand out from the others so here's a list of them, but make sure you research them to see if they meet your needs.</p>
<ul>
<li><a target="_blank" href="https://smartproxy.com/">https://smartproxy.com/</a></li>
<li><a target="_blank" href="https://www.megaproxy.com/">https://www.megaproxy.com/</a></li>
<li><a target="_blank" href="https://whoer.net/webproxy">https://whoer.net/webproxy</a></li>
<li><a target="_blank" href="https://www.proxysite.com/">https://www.proxysite.com/</a></li>
<li><a target="_blank" href="https://hide.me/en/proxy">https://hide.me/en/proxy</a></li>
<li><a target="_blank" href="https://www.kproxy.com/">https://www.kproxy.com/</a></li>
<li><a target="_blank" href="https://www.vpnbook.com/webproxy">https://www.vpnbook.com/webproxy</a></li>
</ul>
<h2 id="heading-proxy-server-vs-vpn">Proxy server vs VPN</h2>
<p>If you're familiar with VPNs (virtual private networks), then you might be wondering how a proxy is different. The main difference is that a VPN secures all of your network traffic where proxies only secure your internet traffic. </p>
<p>Some things that VPNs secure that proxies don’t include FTP uploads or downloads and background operating system processes, like updates.</p>
<p>The only thing proxies and VPNs have in common is that they make your internet traffic look like it's coming from a different IP address. That is all they have in common. The way they handle this is wildly different because of what they're used for.</p>
<p>A proxy just passes along your internet requests, acting like a middleman. A VPN on the other hand tunnels all of your network activity down to the operating system level. Proxies are typically used by a single application like a browser or torrenting client.</p>
<p>Companies tend to use VPNs to let employees access corporate resources without worrying about the traffic being intercepted or recorded by an ISP (internet service provider). These are usually hosted on a physical computer somewhere on premises.</p>
<p>The great thing about VPNs is that they conceal everything you do. If your ISP were to get a history of your usage, it would only see that you were connected to a VPN. Nothing about your traffic would be seen. When you connect to public Wi-Fi, a VPN is the most secure choice.</p>
<p>With all of the benefits that come with using a VPN, there are still good reasons people choose proxies. To start with, VPNs are typically more expensive than a proxy. You also need decent computer hardware to run a VPN. The connection is usually slower than a proxy would be.</p>
<p>There are plenty of times when you don’t necessarily need the kind of security that a VPN offers. When you just want to mask your activities on a single application at a low cost, a proxy might be worth considering.</p>
<h2 id="heading-benefits-and-risks">Benefits and risks</h2>
<p>Now that you know everything about proxies, here's a list of some of the benefits and risks associated with using them.</p>
<h3 id="heading-benefits">Benefits</h3>
<ul>
<li>Secure and private internet browsing</li>
<li>Ability to get around geo-location restrictions</li>
<li>Better network performance</li>
<li>Ability to control what websites clients have access to</li>
<li>Many types to choose from to suit specific needs</li>
</ul>
<h3 id="heading-risks">Risks</h3>
<ul>
<li>Your requests might return really slow</li>
<li>Not all proxies encrypt your requests so your information could still leak out</li>
<li>Free or cheap proxies could be set up by hackers or government agencies</li>
<li>Proxies can disappear at any time</li>
<li>All of your requests and information always go through a third party that could be run by anyone</li>
</ul>
<p>There are plenty more benefits and risks to using any of the proxy server types. That's why it is important to only connect to proxy servers you trust. When you are connected to a trusted proxy, the risks should have been taken into account in the configurations so you have less to worry about.</p>
<h2 id="heading-how-to-set-up-a-simple-proxy-server">How to set up a simple proxy server</h2>
<p>Creating your own private proxy sounds a lot harder than it is. You can make a proxy with a computer in your house that's just as secure as most proxies you can buy. It just takes some patience and a little curiosity.</p>
<p>On a Linux server, you can install Squid and set the configurations for the proxy you want to create. You'll be able to do things like block specific websites or require authentication before a client can connect to the proxy. </p>
<p>Here's a great walk through on setting up a Squid proxy on Linux: <a target="_blank" href="https://devopscube.com/setup-and-configure-proxy-server/">https://devopscube.com/setup-and-configure-proxy-server/</a></p>
<p>On Windows and Mac, you have the option of making a proxy server using Python and the <a target="_blank" href="https://cloud.google.com/appengine">Google App Engine</a>. You will have to pay for the Google App Engine service, but they make it fairly affordable. </p>
<p>Setting up a proxy like this is a little more involved than on Linux, but here's a great walk through: <a target="_blank" href="https://www.hongkiat.com/blog/proxy-with-google-app-engine/">https://www.hongkiat.com/blog/proxy-with-google-app-engine/</a></p>
<h2 id="heading-how-to-connect-to-an-existing-proxy">How to connect to an existing proxy</h2>
<p>Connecting to proxies is usually a straightforward process once you know your proxy's information, like its IP address and port number. No matter what operating system you use, proxies are usually quick to set up.</p>
<p>Typically you'll go into your network settings and find where you can enter your proxy information. Then you should be able to connect and a web page might appear if there's an authentication step included by the proxy. Here's what it looks like on Windows and Ubuntu.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/image-242.png" alt="Image" width="600" height="400" loading="lazy">
<em>setting up a proxy server though the Windows Settings</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/ubuntu-proxy.png" alt="Image" width="600" height="400" loading="lazy">
<em>setting up a proxy server through the Ubuntu Network Settings</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Now you know everything about proxy servers from what they are to how to create one for yourself! I have a little proxy set up on my home network and it does make some things a lot easier to access when I'm away from home.</p>
<p>I write about other random stuff in tech too, like machine learning and VR. You should follow me on <a target="_blank" href="https://twitter.com/FlippedCoding">Twitter</a> to learn stuff that is just cool sometimes. Plus <a target="_blank" href="https://flippedcoding.com">I've got a website</a> where you can check out my other articles and watch my slow transition from the current site to my shiny new one.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to speed up your website - 5 ultimate tips ]]>
                </title>
                <description>
                    <![CDATA[ By Mehul Mohan Site speed is important. It affects your users' experience directly, and a good user experience is extremely important to help drive sales and keep customers and visitors happy. Not only does it improve your site's UX, but a correctly ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/speed-up-website/</link>
                <guid isPermaLink="false">66d4607755db48792eed3f93</guid>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 31 Mar 2020 01:12:49 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/03/Screenshot-2020-03-31-at-12.47.19-AM.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Mehul Mohan</p>
<p>Site speed is important. It affects your users' experience directly, and a good user experience is extremely important to help drive sales and keep customers and visitors happy. Not only does it improve your site's UX, but a correctly configured website eats fewer resources and offloads work from the main server as well.</p>
<p>In this post, I'll discuss what I learned while getting an A grade on GTMetrix on <a target="_blank" href="https://codedamn.com">codedamn</a> - a platform for developers to learn programming in a new way. Let's start!</p>
<h2 id="heading-1-compress-images">#1: Compress Images</h2>
<p>Raw images are highly compressible, and you'd be surprised to find out how much bandwidth and data you could save just by compressing all your images. </p>
<p>For production sites, I recommend tools like <a target="_blank" href="https://www.npmjs.com/package/tinypng-cli">TinyPNG CLI</a> which can compress your images on server in a directory. Here's a quick peek of how TinyPNG saved over 2MB on a high res photo I uploaded on their site:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/Screenshot-2020-03-31-at-12.45.11-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The great thing is TinyPNG allows 500 images free per month, so for a small to medium site, you can almost always stay inside the free quota!</p>
<h2 id="heading-2-code-splitting-and-bundle-splitting">#2: Code Splitting AND Bundle Splitting</h2>
<p>Code Splitting means that you split your code up into various chunks and lazily loading them when needed. You always need this on your site</p>
<p>Bundle Splitting refers to splitting individual files generated by code splitting further down into smaller bundles. This improves caching on browsers as well as bytecode caching by browsers.</p>
<p>Although bundle splitting can be frowned upon as you increase HTTP requests, HTTP/2 has little to no impact on multiple requests until concurrency limits hit. And fortunately concurrency limits are in the range of hundreds of requests. </p>
<p>So if you're not making hundreds of HTTP requests (which you're probably not), you should be good with bundle splitting.</p>
<p>For code and bundle splitting, you need to integrate your project with a module bundler like parcel or webpack. </p>
<p>Once you configure them correctly, they work like magic. And they can really bring down the load both on your server and on the client's browsers. </p>
<p>They do this by caching resources and not downloading resources you don't currently need - like, why would you want to send the JS code for <code>/about</code> route when I'm on <code>/feedback</code> route.</p>
<p>You should also minify your JS/CSS builds. For webpack, there are plugins like UglifyJS available. This helps reduce the size by trimming whitespaces, comments, shortening code, and so on – which will eventually reduce the size of the contents.</p>
<h2 id="heading-3-do-not-use-shared-hosting-services">#3: Do not use shared hosting services</h2>
<p>If you're a developer or someone who has a basic understanding of how to work with bash a little, there's simply no reason to go with shared hosting services. </p>
<p>In almost every case, either you can host your static assets for free on sites like GitHub pages, or you can go with more controlled options like cloud hosting. Almost all these cloud hosting players like AWS, Google Cloud, and DigitalOcean provide so many free credits that you can consume a lot of their services for free for a long time!</p>
<p>In some cases, shared hosting is cheaper than alternatives like $5 DigitalOcean hosting. But these servers have very limited resources allocated to your website, which degrades the overall site performance. Jailed shells, shared vCPUs, and limited RAM are some things to begin with. </p>
<p>Since we, as developers, always want to be in control, and going with a IaaS or a PaaS is always a solution.</p>
<p>Although you can go with any cloud provider of your choice, I would recommend DigitalOcean - the one I use for <a target="_blank" href="https://codedamn.com">codedamn</a> currently. It is dead simple to setup, and you can get <a target="_blank" href="https://codedamn.com/go/digitalocean">$100 worth of free cloud credits with this link</a>.</p>
<p>Choosing a cloud provider can really give you resources and infrastructure which will automatically bump your server performance (and your site performance) a bit.</p>
<h2 id="heading-4-set-http-expiry-headers">#4: Set HTTP Expiry headers</h2>
<p>Just like we discussed above, caching is extremely important to setup correctly. HTTP expiry headers inform the browser what to cache and for how long. Cached resources are not fetched from the remote servers, so it's important not to cache main entrypoint resources like <code>index.html</code> - the first file you serve. You must also implement cache busting appropriately. </p>
<p>Again, for module bundlers like webpack, you can implement cache busting by having <code>[contenthash]</code> in the name of the bundles it spits out. Also, this requires that browsers never cache your <code>index.html</code> or any other HTML file you're using as an entrypoint to your site. How can we achieve that?</p>
<p>For static file serving and HTTP expiry headers, use NGiNX. NGiNX can manage all of that stuff for you. Here's a sample configuration recommended for setting HTTP expiry headers:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/Screenshot-2020-03-31-at-12.32.38-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This configuration simply sets caching off for <code>text/html</code> resources. For others, it sets it to the maximum possible time the browser can hold the cache. </p>
<p>Note that this configuration strongly relies on the fact that you have your cache busting mechanisms implemented in your module bundler (like the <code>[contenthash]</code> we talked about above).</p>
<h2 id="heading-5-enable-brotli-compression">#5: Enable Brotli Compression</h2>
<p>Brotli compression is a compression algorithm designed by Google, which is 20-25% more efficient than the well-known GZIP compression. </p>
<p>Brotli compression can be implemented on sites, again, using NGiNX. Let's take a look at an example of what happens when Brotli compression is enabled:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/Screenshot-2020-03-31-at-12.40.32-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The first file size is 3.33 MB - without compression.</p>
<p>When it is compressed using Brotli - the size drops to 572KB.</p>
<p>When it is compressed using GZIP - the size only drops to 783KB.</p>
<p>Brotli saves over 200KB compared to GZIP, and over 2.5MB compared to no compression. And it is not even performing at its peak! That's over 82% compression! Very cool.</p>
<h2 id="heading-thats-it">That's it!</h2>
<p>That's basically it! Without going much into the details of how to implement these specific techniques, we covered some of the most important things to consider when speeding up your site. </p>
<p>I did not go into the specific details as your mileage may vary with your server setups and configurations - but the general concept remains same.</p>
<p>Make sure you follow these best practices and let me know what you think by saying Hi on my <a target="_blank" href="https://twitter.com/mehulmpt">twitter handle</a>.</p>
<p>If you liked this article, let's meet on social media. Here's my <a target="_blank" href="https://instagram.com/mehulmpt">Instagram</a> and <a target="_blank" href="https://twitter.com/mehulmpt">Twitter</a>. I'm super active, and would love to have a chat! Let's connect.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
