<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Stephen Hyde</title>
    <description>The latest articles on DEV Community by Stephen Hyde (@birdhighway).</description>
    <link>https://dev.to/birdhighway</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F559483%2F44ab9e9a-501b-4888-ae6e-33835dc1bf0c.jpeg</url>
      <title>DEV Community: Stephen Hyde</title>
      <link>https://dev.to/birdhighway</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/birdhighway"/>
    <language>en</language>
    <item>
      <title>Reverse Proxy with SSL</title>
      <dc:creator>Stephen Hyde</dc:creator>
      <pubDate>Sun, 14 Aug 2022 21:08:00 +0000</pubDate>
      <link>https://dev.to/birdhighway/reverse-proxy-with-ssl-2h32</link>
      <guid>https://dev.to/birdhighway/reverse-proxy-with-ssl-2h32</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;It's often useful to make an application under local development available temporarily on the public internet. At &lt;a href="https://www.blendededge.com/"&gt;Blended Edge&lt;/a&gt;, for example, our work with automating integrations means we run a lot of OAuth2 flows, and using OAuth2 for an application under development (usually) requires a publicly available endpoint with an SSL certificate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ngrok.com/"&gt;Ngrok&lt;/a&gt; is a reverse proxy tool that makes this very simple, but it's also fairly easy to set up a reverse proxy using an AWS EC2 instance, certbot from Let's Encrypt, and the &lt;code&gt;ssh&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;This walk-through assumes some basic knowledge of DNS, &lt;code&gt;ssh&lt;/code&gt;, and EC2 instances (or similar cloud compute resources).&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Remote Server
&lt;/h2&gt;

&lt;p&gt;We're going to start by launching an EC2 instance on AWS to act as our remote server. This could of course be done with any cloud provider, such as Azure or Digital Ocean.&lt;/p&gt;

&lt;p&gt;Set the instance name as "Reverse-Proxy", and under "Application and OS Images" select Ubuntu. At the time of writing this the default Ubuntu image is 22.04.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PJRvUld4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g71ihqglyuayfd4gfzhr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PJRvUld4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g71ihqglyuayfd4gfzhr.png" alt="Create a new Ubuntu instance" width="855" height="1249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have an existing SSH key pair that I will use for accessing this instance, but if you don't have one or would like to use a new key pair simply click "Create new key pair".&lt;/p&gt;

&lt;p&gt;Create a new security group that allows HTTP/HTTPS traffic from any IP address, but limit the SSH traffic to just your own IP.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kdls2KDN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4hntbrsln6v4mgyzpzob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kdls2KDN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4hntbrsln6v4mgyzpzob.png" alt="Set up the security group" width="834" height="944"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click "Launch Instance", navigate to the instances overview page, and wait for the status checks on the new instance to pass. This may take a couple of minutes.&lt;/p&gt;

&lt;p&gt;Once the instance is ready, go to the overview page for the instance and click "Connect" in the upper right hand corner. Follow the steps to connect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# if this is a new ssh key you may need to specify the path to the key&lt;/span&gt;
&lt;span class="c"&gt;# ssh -i /path/to/key.pem &amp;lt;user&amp;gt;@&amp;lt;address&amp;gt;&lt;/span&gt;
ssh ubuntu@ec2-13-59-152-133.us-east-2.compute.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install Nginx
&lt;/h2&gt;

&lt;p&gt;Update the package list, upgrade the packages, and install nginx.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you send a GET request to your instance's IP address via a web browser or curl you should get the default nginx page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl http://13.59.152.133
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Welcome to nginx!&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Domain and SSL
&lt;/h2&gt;

&lt;p&gt;Point a domain to the public IP address of your instance. The nameservers for my domain &lt;code&gt;hydewd.com&lt;/code&gt; are with DigitalOcean, so in my case updating the &lt;code&gt;A&lt;/code&gt; record to point the domain at my instance looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MUirwsGj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8isbg6158d89raghlrmj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MUirwsGj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8isbg6158d89raghlrmj.png" alt="DNS A Record" width="880" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: If you are doing any kind of serious or long term work with this setup you should allocate an elastic IP address, associate it with your instance, and point your domain to the elastic IP.&lt;/p&gt;

&lt;p&gt;Install &lt;code&gt;certbot&lt;/code&gt; from Let's Encrypt to handle automatically setting up the SSL certificate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;certbot python3-certbot-nginx &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run have &lt;code&gt;certbot&lt;/code&gt; make the request for an SSL certificate. Here I am specifying that I want to cover both the naked domain &lt;code&gt;hydewd.com&lt;/code&gt; as well as &lt;code&gt;www.hydewd.com&lt;/code&gt;. You will be prompted to provide an email address for renewal notices, asked to agree to the terms of service, and asked if you are willing to share your email with the Electronic Frontier Foundation (this last one is optional).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; hydewd.com &lt;span class="nt"&gt;-d&lt;/span&gt; www.hydewd.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything has worked you should now be able to load the default nginx page at &lt;code&gt;https://&amp;lt;your-domain&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nginx Configuration
&lt;/h2&gt;

&lt;p&gt;The next step is to tell nginx to redirect http/https requests to another port (&lt;code&gt;8080&lt;/code&gt;). In the following step we will tunnel that port to our local machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vi /etc/nginx/sites-available/default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within the &lt;code&gt;location&lt;/code&gt; block of the default &lt;code&gt;server&lt;/code&gt; block, add a line to forward traffic to &lt;code&gt;http://127.0.0.1:8080&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We will be changing the contents of the &lt;code&gt;location&lt;/code&gt; block. Here is what it looks like to begin with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the contents of the &lt;code&gt;location&lt;/code&gt; block to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_pass http://127.0.0.1:8080/;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file, and ensure that the configuration is valid with the command &lt;code&gt;sudo nginx -t&lt;/code&gt;. If there is no error, go ahead and restart nginx to apply the latest changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-s&lt;/span&gt; reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  SSH Remote Forwarding
&lt;/h2&gt;

&lt;p&gt;The final step is to tunnel the requests from the server's port &lt;code&gt;8080&lt;/code&gt; to the local machine. This can be done with the &lt;code&gt;ssh&lt;/code&gt; remote forwarding command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-R&lt;/span&gt; 8080:localhost:8080 ubuntu@ec2-18-217-182-155.us-east-2.compute.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run a server locally on port &lt;code&gt;8080&lt;/code&gt;. Here, for example, is a simple node server using the express framework that will reply to all &lt;code&gt;GET&lt;/code&gt; requests with some information about what the server sees in the incoming request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;local-server &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;local-server
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a file called &lt;code&gt;index.js&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`App listening at http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sending a query to your domain will now return a response from your local express server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'https://www.hydewd.com/some/path?some=query&amp;amp;another=123'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "baseUrl": "",
  "headers": {
    "host": "www.hydewd.com",
    "x-real-ip": "104.1.2.3",
    "connection": "close",
    "user-agent": "curl/7.68.0",
    "accept": "*/*"
  },
  "hostname": "www.hydewd.com",
  "ip": "::ffff:127.0.0.1",
  "method": "GET",
  "originalUrl": "/some/path?some=query&amp;amp;another=123",
  "path": "/some/path",
  "protocol": "http"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the header &lt;code&gt;x-real-ip&lt;/code&gt; displays the IP address of the end user making the request, not the IP of the instance. This is a result of the configuration line &lt;code&gt;proxy_set_header X-Real-IP $remote_addr;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This approach will work for some basic use cases, but is overall quite limiting. In a future post we will look at using Traefik to provide a more feature rich setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Helpful Links
&lt;/h2&gt;

&lt;p&gt;Digital Ocean Guides: &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-20-04"&gt;How to Install Nginx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nginx Docs: &lt;a href="https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/"&gt;Reverse Proxy Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nginx Docs: &lt;a href="https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/"&gt;Installing Lets Encrypt&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>HTTP Protocol Overview</title>
      <dc:creator>Stephen Hyde</dc:creator>
      <pubDate>Sun, 08 Aug 2021 21:21:13 +0000</pubDate>
      <link>https://dev.to/birdhighway/http-protocol-overview-hi8</link>
      <guid>https://dev.to/birdhighway/http-protocol-overview-hi8</guid>
      <description>&lt;p&gt;HTTP is so fundamental to web development that it is often overlooked. Let's take a minute to explore the protocol and gain a better understanding of how the HTTP request-response cycle works.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is HTTP?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;HTTP&lt;/strong&gt;, or &lt;strong&gt;Hypertext Transfer Protocol&lt;/strong&gt;, is an application level protocol for sharing hypertext.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;protocol&lt;/strong&gt; - A set of rules for how to transmit and receive data. If two programs use the same protocol then the receiver will know how to interpret the data from the sender.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;application level&lt;/strong&gt; - A protocol for programs that an end-user interacts with, as opposed to lower level programs that a user would not generally directly interact with. Most commonly a web browser like Google Chrome in the case of HTTP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;hypertext&lt;/strong&gt; - Text documents displayed on a computer that allow for links to other documents. The most common way we lay out the structure of a hypertext document is with &lt;strong&gt;HyperText Markup Language&lt;/strong&gt;, or &lt;strong&gt;HTML&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt; - HTTP is a set of rules for how two high level programs communicate with each other. HTTP follows a &lt;strong&gt;client-server&lt;/strong&gt; model, where a client makes a request and the server sends back a response. The client will usually be a web browser, and the server will usually be a web server, but this is not always the case.&lt;/p&gt;

&lt;h2&gt;
  
  
  GET Requests
&lt;/h2&gt;

&lt;p&gt;The easiest way to see the raw data in a request is with netcat, a command line utility available on most systems as either &lt;code&gt;netcat&lt;/code&gt; or &lt;code&gt;nc&lt;/code&gt;. We will tell netcat to listen on a port, and then any data we send to that port will be dumped into the terminal for us to see.&lt;br&gt;
&lt;code&gt;nc -l -p 3000&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-l&lt;/code&gt; means "listen"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-p 3000&lt;/code&gt; means "use port 3000"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let's make a simple GET request to this port with &lt;code&gt;curl&lt;/code&gt; in order to see what information is actually sent. With the previous netcat command running in another terminal, run &lt;code&gt;curl localhost:3000&lt;/code&gt;. (Curl defaults to GET if no request method is specified.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5iJjv1pn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s45xz05kqfgudem0tijh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5iJjv1pn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s45xz05kqfgudem0tijh.png" alt="curl sends a GET request to netcat"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The request sent by curl contains four lines of information, and netcat logs them to the terminal. The first line contains basic information about the request: &lt;code&gt;GET / HTTP/1.1&lt;/code&gt; The technical term is the "start line". More specifically, the start line of a request is the "request line", and the start line of a response is the "status line".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET&lt;/code&gt; the HTTP verb, specifies the type of request&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/&lt;/code&gt; the request target, specifies the resource we want - could be "/", "index.html", or "/api/users"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HTTP/1.1&lt;/code&gt; the protocol and the specific version - could be HTTP or HTTPS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the start line we have three lines specifying different http header properties. They tell us more information about the request curl is making. Finally, it's easy to overlook the fourth line, which is simply an empty line signalling that we have reached the end of the request header. &lt;/p&gt;

&lt;p&gt;This is a GET request, so there is no body, and we have reached the end of the request, but both the curl command and the netcat command are hanging rather than returning control to the user. Netcat is simply listening without responding, and will keep listening until the connection is closed, while curl is waiting until it either receives a response or the request it made times out. We can interrupt the curl command, which ends the connection, allowing netcat to exit as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wNVgEcxm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hzj1rwcc0z6hv8k6tpwj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wNVgEcxm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hzj1rwcc0z6hv8k6tpwj.png" alt="the terminal after curl and netcat exit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Header: Host
&lt;/h2&gt;

&lt;p&gt;The first header listed in the example above is &lt;code&gt;Host: localhost:3000&lt;/code&gt;, which tells us the name (and optionally the port) of the server we are requesting. If we had multiple websites that we wanted to run on a server with only one IP address we could use the Host header to differentiate which requests went to which website. This can be configured relatively easily in Apache with virtual hosts or in Nginx with multiple server blocks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Header: User-Agent
&lt;/h2&gt;

&lt;p&gt;The User-Agent header provides information about the client making the request, and might specify the program name and the operating system. In this case we see the name of the program making the request, "curl", and the version, "7.68.0": &lt;code&gt;User-Agent: curl/7.68.0&lt;/code&gt;. But this field can be confusing, especially when it comes to web browsers. &lt;a href="https://www.howtogeek.com/114937/htg-explains-whats-a-browser-user-agent/"&gt;Here&lt;/a&gt; is an interesting explanation of why Google Chrome's user agent string starts with "Mozilla".&lt;/p&gt;

&lt;h2&gt;
  
  
  Header: Accept
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;media type&lt;/strong&gt; or &lt;strong&gt;MIME type&lt;/strong&gt; (Multipurpose Internet Mail Extensions) is a string declaring the format of an http body. It consists of two parts: the type and the subtype, separated by a forward slash. An asterisk is used as a wildcard. &lt;/p&gt;

&lt;p&gt;The Accept header specifies what MIME types a client is capable of receiving for a specific request, like &lt;code&gt;text/html&lt;/code&gt; or &lt;code&gt;image/jpeg&lt;/code&gt;. Multiple values can be given as a comma separated list. In our example above &lt;code&gt;Accept: */*&lt;/code&gt; means the client will accept any media type with any subtype.&lt;/p&gt;

&lt;p&gt;We will see MIME types again when we look at the Content-Type header of a POST request.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Connection
&lt;/h2&gt;

&lt;p&gt;HTTP concerns itself with the formatting and handling of a request followed by a response, but it does not concern itself with the details of how a connection is made between the two programs, or how the data is actually transmitted over the wire. These details are handled by lower-level protocols, which for our purposes will almost always be &lt;strong&gt;TCP/IP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the below image we can see a Wireshark capture of the actual packets sent. The first three lines establish a connection in a process known as the TCP Three-Way Handshake, which consists of the client sending a packet with the SYN flag set (SYNchronize), the server responding with a SYN ACK packet (SYNchronize ACKnowledge), and finally the client sending a packet to ACKnowledge receiving the SYN ACK packet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qs81bo2F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3gqjd0omrx1mv0nqpj0n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qs81bo2F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3gqjd0omrx1mv0nqpj0n.png" alt="tcp packets in wireshark"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is somewhat analogous to the way a phone call starts, establishing a connection but not sharing any actual information yet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client sends SYN - Alice dials Bob's number&lt;/li&gt;
&lt;li&gt;Server sends SYN ACK - Bob picks up the phone and says "Hello?"&lt;/li&gt;
&lt;li&gt;Client sends ACK - Alice says "Hello" back &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fourth line, highlighted in green, is the packet that carries the actual HTTP request from curl to netcat, and the following line is netcat's acknowledgement to curl that it received that particular packet.&lt;/p&gt;

&lt;p&gt;The transmission of those first five packets occurs practically instantly from the human perspective - all within the same millisecond. It's not until we abort the curl request five seconds later that we see the final three lines terminating the TCP connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  POST Requests
&lt;/h2&gt;

&lt;p&gt;Let's see what the HTTP request looks like when it is a POST with some data attached. We have three pieces of information: a name, "Joe Smith", an age, 64, and some letters, "abc". There are several ways we can format this data when sending it to the server (or netcat in this case), and the first one we will examine is &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;URL-encoded form data splits the information up into key value pairs, with the key and value separated by an equals sign &lt;code&gt;=&lt;/code&gt;, and the pairs separated by an ampersand &lt;code&gt;&amp;amp;&lt;/code&gt;. Any characters that are not letters or numbers are escaped with the percent sign &lt;code&gt;%&lt;/code&gt;. So our information becomes: &lt;code&gt;name=Joe%20Smith&amp;amp;age=64&amp;amp;letters=abc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If we send a POST request with a body to netcat we see the headers, followed by a blank line, followed by the request body: &lt;code&gt;curl -X POST -d "name=Joe%20Smith&amp;amp;age=64&amp;amp;letters=abc" localhost:3000&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-X POST&lt;/code&gt; this flag allows us to specify a method other than GET&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-d "..."&lt;/code&gt; include data in the body of the request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--krugQIMy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ap3sp37dlwwnv17euale.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--krugQIMy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ap3sp37dlwwnv17euale.png" alt="curl POST request with data"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Headers: Content-Length &amp;amp; Content-Type
&lt;/h2&gt;

&lt;p&gt;The POST request contains two new headers. &lt;code&gt;Content-Length: 35&lt;/code&gt; tells the recipient that the request contains the header, an empty line, and then 35 bytes of data. But first we need to tell the server how to interpret those 35 bytes of data. Curl adds the header &lt;code&gt;Content-Type: application/x-www-form-urlencoded&lt;/code&gt; by default when you specify data with the &lt;code&gt;-d&lt;/code&gt; flag, but if the data you are sending is of a different type you can override this default with another flag: &lt;code&gt;-H "Content-Type: ..."&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The data itself is just ones and zeros to the computer. Below is an image showing the tail end of a wireshark capture of the POST request. It shows both the raw ones and zeros (taking up most of the width), as well as those bits interpreted as ASCII text (narrow column to the right). The body of the POST is highlighted in blue:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RaQBEoGL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l7w2a5qh7xogmffaqqur.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RaQBEoGL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l7w2a5qh7xogmffaqqur.png" alt="wireshark binary data"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can make it more readable by viewing the bytes in hexadecimal form, where each set of two characters corresponds to eight bits, or one byte. (For example the binary &lt;code&gt;01101110&lt;/code&gt; is &lt;code&gt;6e&lt;/code&gt; in hex, interpreted as &lt;code&gt;n&lt;/code&gt; in ASCII). The body of the request is again highlighted in blue, and I've drawn a red line around the sequence of two new lines that signal the end of the header. Once the server recognizes that the header has ended, it knows that the next 35 bytes belong to the body of the request (the 35 pairs of hex letters highlighted in blue).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fiXNwL-F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ea69ti9d4cfrkf80x27j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fiXNwL-F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ea69ti9d4cfrkf80x27j.png" alt="wireshark hex data"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's important to note that without a content-type header the server does not know (though it may be able to guess) that it should interpret the numbers as ASCII codes, much less that the ASCII characters are in the format of key-value pairs escaped with %.&lt;/p&gt;

&lt;p&gt;We can send JSON data with curl and specify a content-type in a header with the &lt;code&gt;-H&lt;/code&gt; flag. The server still interprets the individual bytes as ASCII codes, but those decoded characters are now treated as JSON rather than url-encoded key value pairs. &lt;code&gt;curl -X POST -d '{"data": "ABC123"}' -H 'Content-Type: application/json' localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The program you are using to send data to the server may or may not automatically determine the right content-type header for your data, and knowing how to set and check headers is an essential skill. To learn more about the HTTP protocol check out the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP"&gt;MDN guide&lt;/a&gt; or read the official standard, &lt;a href="https://datatracker.ietf.org/doc/html/rfc7230"&gt;RFC 7230&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
