{"id":614,"date":"2018-10-31T23:41:17","date_gmt":"2018-10-31T23:41:17","guid":{"rendered":"http:\/\/www.afternerd.com\/blog\/?p=614"},"modified":"2019-02-02T06:36:37","modified_gmt":"2019-02-02T06:36:37","slug":"python-http-server","status":"publish","type":"post","link":"https:\/\/www.afternerd.com\/blog\/python-http-server\/","title":{"rendered":"Python: Let&#8217;s Create a Simple HTTP Server (Tutorial)"},"content":{"rendered":"<p>Web severs are everywhere.<\/p>\n<p>Heck you are interacting with one right now!<\/p>\n<p>No matter what type of software engineer you are, at some point in your career you will have to interact with web servers. May be you are building an API server for a backend service. Or may be you are just configuring a web server for your website.<\/p>\n<p>In this article, I will cover how to create the most basic http web server in Python.<\/p>\n<p>But because I want to make sure you understand what we are building, I am going to give an overview first about what web servers are and how they work.<\/p>\n<p>If you already know how web servers work, then you can <a href=\"#create-simple-html\">skip directly to this section.<\/a><\/p>\n<ul>\n<li><a href=\"#what-is-web-server\">What is an HTTP server?<\/a><\/li>\n<li><a href=\"#tcp-address\">The TCP socket address<\/a><\/li>\n<li><a href=\"#create-simple-html\">Create a simple HTTP file<\/a><\/li>\n<li><a href=\"#create-web-server\">Create an HTTP web server<\/a><\/li>\n<\/ul>\n<h2 id=\"what-is-web-server\">What is an HTTP Server?<\/h2>\n<p>An HTTP web server is nothing but a process that is running on your machine and does exactly two things:<\/p>\n<p>1- <em>Listens<\/em> for incoming http requests on a specific TCP socket address (IP address and a port number which I will talk about later)<\/p>\n<p>2- <em>Handles<\/em> this request and sends a response back to the user.<\/p>\n<p>Let me make my point less abstract.<\/p>\n<p>Imagine you pull up your Chrome browser and type <strong>www.yahoo.com<\/strong> in the address bar.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"post-img alignnone wp-image-1024 size-full\" src=\"http:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-3.39.26-PM.png\" alt=\"\" width=\"1954\" height=\"572\" srcset=\"https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-3.39.26-PM.png 1954w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-3.39.26-PM-300x88.png 300w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-3.39.26-PM-768x225.png 768w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-3.39.26-PM-1024x300.png 1024w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-3.39.26-PM-200x59.png 200w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-3.39.26-PM-400x117.png 400w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-3.39.26-PM-600x176.png 600w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-3.39.26-PM-800x234.png 800w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-3.39.26-PM-1200x351.png 1200w\" sizes=\"(max-width: 1954px) 100vw, 1954px\" \/><\/p>\n<p>Of course you are going to get the Yahoo home page rendered on your browser window.<\/p>\n<p><em>But what really just happened under the hood?<\/em><\/p>\n<p>Actually a lot of things have happened and I might dedicate a whole article to explain the magic behind how this happened.<\/p>\n<p>But for the sake of simplicity, I will abstract away some of the details and talk about this at a\u00a0<em>very<\/em> high level.<\/p>\n<p>At a high level, when you type <strong>www.yahoo.com\u00a0<\/strong>on your browser, your browser will create a network message called an <a href=\"https:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec5.html\" target=\"_blank\" rel=\"noopener\">HTTP request<\/a>.<\/p>\n<p>This Request will travel all the way to a Yahoo computer that has a web server running on it. This web server will intercept your request, and handle it by responding back with the html of the Yahoo home page.<\/p>\n<p>Finally your browser renders this html on the screen and that&#8217;s what you see on your screen.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"post-img alignnone wp-image-1026 size-full\" src=\"http:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/http.png\" alt=\"\" width=\"1200\" height=\"628\" srcset=\"https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/http.png 1200w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/http-300x157.png 300w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/http-768x402.png 768w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/http-1024x536.png 1024w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/http-200x105.png 200w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/http-400x209.png 400w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/http-600x314.png 600w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/http-800x419.png 800w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/p>\n<p>Every interaction with the Yahoo home page after that (for example, when you click on a link) initiates a new request and response exactly like the first one.<\/p>\n<p>To reiterate, the machine that receives the http request has a software process called a web server running on it. This web server is responsible for <strong>intercepting these requests<\/strong> and <strong>handling them appropriately<\/strong>.<\/p>\n<p>Alright, now that you know what a web server is and what its function is exactly, you might be wondering how does the request reach that yahoo machine in the first place?<\/p>\n<p>Good question!<\/p>\n<p>In fact this is one of my favorite questions that I ask potential candidates in a <a href=\"https:\/\/www.afternerd.com\/blog\/coding-interview\/\" target=\"_blank\" rel=\"noopener\">coding interview<\/a>.<\/p>\n<p>Let me explain how, but again&#8230;.at a high level.<\/p>\n<h2 id=\"tcp-address\">The TCP Socket Address<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"post-img alignnone wp-image-1033 size-full\" src=\"http:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/tcpsocketaddressing.png\" alt=\"\" width=\"1200\" height=\"628\" srcset=\"https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/tcpsocketaddressing.png 1200w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/tcpsocketaddressing-300x157.png 300w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/tcpsocketaddressing-768x402.png 768w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/tcpsocketaddressing-1024x536.png 1024w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/tcpsocketaddressing-200x105.png 200w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/tcpsocketaddressing-400x209.png 400w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/tcpsocketaddressing-600x314.png 600w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/tcpsocketaddressing-800x419.png 800w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/p>\n<p>Any http message (whether it is a request or response) needs to know how to reach its destination.<\/p>\n<p>In order to reach its destination, each http message carries an address called the destination <em>TCP address<\/em>.<\/p>\n<p>And each <em>TCP address<\/em> is composed of an<strong> IP address<\/strong> and a <strong>port number<\/strong>.<\/p>\n<p>I know all these acronyms (TCP, IP, etc..) might be overwhelming if your networking concepts are not strong.<\/p>\n<p>I will try to keep it simple but if you are interested in improving your knowledge of networking concepts, <a href=\"https:\/\/amzn.to\/2Gi7awg\" target=\"_blank\" rel=\"noopener\">I highly recommend this book by Ross and Kurose<\/a>.<\/p>\n<p>So where is that address when all you did was type <strong>www.yahoo.com<\/strong>\u00a0on your browser?<\/p>\n<p>Well, this domain name is converted into an IP address through a large distributed database called the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Domain_Name_System\" target=\"_blank\" rel=\"noopener\">DNS<\/a>.<\/p>\n<p><em>Do you want to check out what this IP address is?<\/em><\/p>\n<p>Easy! Head to your terminal and do the following:<\/p>\n<pre class=\"prettyprint\"><code>$ host yahoo.com\r\nyahoo.com has address 98.138.219.231\r\nyahoo.com has address 98.137.246.8\r\nyahoo.com has address 98.138.219.232\r\nyahoo.com has address 72.30.35.9\r\nyahoo.com has address 98.137.246.7\r\nyahoo.com has address 72.30.35.10\r\nyahoo.com has IPv6 address 2001:4998:44:41d::3\r\nyahoo.com has IPv6 address 2001:4998:c:1023::5\r\nyahoo.com has IPv6 address 2001:4998:c:1023::4\r\nyahoo.com has IPv6 address 2001:4998:58:1836::10\r\nyahoo.com has IPv6 address 2001:4998:58:1836::11\r\nyahoo.com has IPv6 address 2001:4998:44:41d::4\r\nyahoo.com mail is handled by 1 mta5.am0.yahoodns.net.\r\nyahoo.com mail is handled by 1 mta6.am0.yahoodns.net.\r\nyahoo.com mail is handled by 1 mta7.am0.yahoodns.net.<\/code><\/pre>\n<p>As you can see, the DNS will translate <strong>yahoo.com<\/strong> to any of the addresses above.<\/p>\n<p>The IP address alone will allow the HTTP message to arrive at the right machine, but you still need the port number in order for the HTTP request to arrive exactly at the web server.<\/p>\n<p>In other words, the web server is a regular network application that is listening on a specific port.<\/p>\n<p>And the http request MUST be addressed to that port.<\/p>\n<p>So where is the port number when you type <strong>www.yahoo.com<\/strong>?<\/p>\n<p>By default, the port number is 80 for <strong>http<\/strong> and 443 for <strong>https<\/strong>, so even though you haven&#8217;t explicitly specified the port number, it is still there.<\/p>\n<p>And if the web server is listening on a non-default port number (neither 80 nor 443), you must explicitly specify the port number like this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"post-img alignnone wp-image-1032 size-full\" src=\"http:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-7.07.38-PM.png\" alt=\"\" width=\"1862\" height=\"572\" srcset=\"https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-7.07.38-PM.png 1862w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-7.07.38-PM-300x92.png 300w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-7.07.38-PM-768x236.png 768w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-7.07.38-PM-1024x315.png 1024w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-7.07.38-PM-200x61.png 200w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-7.07.38-PM-400x123.png 400w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-7.07.38-PM-600x184.png 600w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-7.07.38-PM-800x246.png 800w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-30-at-7.07.38-PM-1200x369.png 1200w\" sizes=\"(max-width: 1862px) 100vw, 1862px\" \/><br \/>\nBy now you should have all the necessary information to create an http web server in Python.<\/p>\n<p>So without further ado, let&#8217;s get started.<\/p>\n<h2 id=\"create-simple-html\">Create a simple HTML file<\/h2>\n<p>Here is what we want to do.<\/p>\n<p>We want to create a simple http server that serves a static html web page.<\/p>\n<p>Let&#8217;s create our html page.<\/p>\n<pre class=\"prettyprint\"><code>&lt;html&gt;\r\n    &lt;head&gt;\r\n        &lt;title&gt;Python is awesome!&lt;\/title&gt;\r\n    &lt;\/head&gt;\r\n    &lt;body&gt;\r\n        &lt;h1&gt;Afternerd&lt;\/h1&gt;\r\n        &lt;p&gt;Congratulations! The HTTP Server is working!&lt;\/p&gt;\r\n    &lt;\/body&gt;\r\n&lt;\/html&gt;<\/code><\/pre>\n<p>Now go ahead and save this file as <span class=\"symbol\">index.html<\/span><\/p>\n<p>With the web page that we want to serve out of the way, the next step is to create a web server that will serve this html page.<\/p>\n<h2 id=\"create-web-server\">Create an HTTP web server<\/h2>\n<p>In order to create a web server in <strong>Python 3<\/strong>, you will need to import two modules: <span class=\"symbol\">http.server<\/span> and <span class=\"symbol\">socketserver<\/span><\/p>\n<div class=\"side-note\"><img decoding=\"async\" src=\"\/favicon.ico\" \/><br \/>\nNotice that in <strong>Python 2<\/strong>, there was a module named\u00a0<a href=\"https:\/\/docs.python.org\/2\/library\/simplehttpserver.html\" target=\"_blank\" rel=\"noopener\">SimpleHTTPServer<\/a>. This module has been merged into http.server in <strong>Python 3<\/strong><\/div>\n<p>Let&#8217;s take a look at the code to create an http server<\/p>\n<pre class=\"prettyprint\"><code>import http.server\r\nimport socketserver\r\n\r\nPORT = 8080\r\nHandler = http.server.SimpleHTTPRequestHandler\r\n\r\nwith socketserver.TCPServer((\"\", PORT), Handler) as httpd:\r\n    print(\"serving at port\", PORT)\r\n    httpd.serve_forever()<\/code><\/pre>\n<p>Just like that we have a functional http server.<\/p>\n<p>Now let&#8217;s dissect this code line-by-line.<\/p>\n<p>First, as I mentioned earlier, a web server is a process that listens to incoming requests on specific TCP address.<\/p>\n<p>And as you know by now a TCP address is identified by an <em>ip address<\/em> and a <em>port number<\/em>.<\/p>\n<p>Second, a web server also needs to be told how to handle incoming requests.<\/p>\n<p>These incoming requests are handled by special handlers. You can think of a web server as a dispatcher, a request comes in, the http server inspects the request and dispatches it to a designated handler.<\/p>\n<p>Of course these handlers can do anything you desire.<\/p>\n<p><em>But what do you think the most basic handler is?<\/em><\/p>\n<p>Well, that would be a handler that just serves a static file.<\/p>\n<p>In other words, when I go to <strong>yahoo.com<\/strong>, the web server at the other end sends back a static html file.<\/p>\n<p>This is in fact what we are exactly trying to do.<\/p>\n<p>And that, my friend, is what the <span class=\"symbol\">http.server.SimpleHTTPRequestHandler<\/span> is: a simple HTTP request handler that serves files from <strong>the current directory<\/strong> and <strong>any of its subdirectories<\/strong>.<\/p>\n<p>Now let&#8217;s talk about the <strong>socketserver.TCPServer<\/strong> class.<\/p>\n<p>An instance of <strong>TCPServer<\/strong> describes a server that uses the TCP protocol to send and receive messages (http is an application layer protocol on top of TCP).<\/p>\n<p>To instantiate a TCP Server, we need two things:<\/p>\n<p><em>1- The TCP address (IP address and a port number)<\/em><\/p>\n<p><em>2- The handler<\/em><\/p>\n<pre class=\"prettyprint\"><code>socketserver.TCPServer((\"\", PORT), Handler)<\/code><\/pre>\n<p>As you can see, the TCP address is passed as a tuple of <span class=\"symbol\">(ip address, port number)<\/span><\/p>\n<p>Passing an empty string as the ip address means that the server will be listening on any network interface (all available IP addresses).<\/p>\n<p>And since <strong>PORT<\/strong> stores the value of 8080, then the server will be listening on incoming requests on that port.<\/p>\n<p>For the handler, we are passing the simple handler that we talked about earlier.<\/p>\n<pre class=\"prettyprint\"><code>Handler = http.server.SimpleHTTPRequestHandler\r\n<\/code><\/pre>\n<p>Well, how about <span class=\"symbol\">serve_forever<\/span>?<\/p>\n<p><strong>serve_forever<\/strong> is a method on the TCPServer instance that starts the server and begins listening and responding to incoming requests.<\/p>\n<p>Cool, let&#8217;s save this file as <strong>server.py<\/strong> in <em>the same directory<\/em> as <strong>index.html<\/strong> because by default the <strong>SimpleHTTPRequestHandler<\/strong> will look for a file named <strong>index.html<\/strong> in the current directory.<\/p>\n<p>In that directory, start the web server:<\/p>\n<pre class=\"prettyprint\"><code>$ python server.py\r\nserving at port 8080<\/code><\/pre>\n<p>By doing that, you now have an HTTP server that is listening on any interface at port 8080 waiting for incoming http requests.<\/p>\n<p>It&#8217;s time now for the fun stuff!<\/p>\n<p>Open your browser and type localhost:8080 in the address bar.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"post-img alignnone wp-image-1012 size-full\" src=\"http:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-29-at-5.48.05-PM.png\" alt=\"\" width=\"1086\" height=\"468\" srcset=\"https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-29-at-5.48.05-PM.png 1086w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-29-at-5.48.05-PM-300x129.png 300w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-29-at-5.48.05-PM-768x331.png 768w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-29-at-5.48.05-PM-1024x441.png 1024w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-29-at-5.48.05-PM-200x86.png 200w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-29-at-5.48.05-PM-400x172.png 400w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-29-at-5.48.05-PM-600x259.png 600w, https:\/\/www.afternerd.com\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-29-at-5.48.05-PM-800x345.png 800w\" sizes=\"(max-width: 1086px) 100vw, 1086px\" \/><\/p>\n<p>Awesome! Looks like everything is working fine.<\/p>\n<p>But hey what is <strong>localhost<\/strong>?<\/p>\n<p><b>localhost<\/b> is a host name\u00a0that means\u00a0<i>this computer<\/i>. It is used to access the network services that are running on the host via the loopback network interface.<\/p>\n<p>And since the web server is listening on <strong>any<\/strong> interface, it is also listening on the loopback interface.<\/p>\n<p>You want to know what IP address corresponds to localhost?<\/p>\n<p>You got it.<\/p>\n<pre class=\"prettyprint\"><code>$ host localhost\r\nlocalhost has address 127.0.0.1\r\nlocalhost has IPv6 address ::1\r\nHost localhost not found: 3(NXDOMAIN)<\/code><\/pre>\n<p>In fact you can totally replace <strong>localhost<\/strong> with <strong>127.0.0.1<\/strong> in your browser and you would still get the same result.<\/p>\n<p>Try it out \ud83d\ude42<\/p>\n<h2>One Final Word<\/h2>\n<p>You can actually start a web server with python without even having to write any scripts.<\/p>\n<p>Just go to your terminal and do the following (but make sure you are on python 3)<\/p>\n<pre class=\"prettyprint\"><code>python -m http.server 8080<\/code><\/pre>\n<p>By default, this server will be listening on all interfaces and on port 8080.<\/p>\n<p>If you want to listen to a specific interface, do the following:<\/p>\n<pre class=\"prettyprint\"><code>python -m http.server 8080 --bind 127.0.0.1<\/code><\/pre>\n<p>Also starting from Python 3.7, you can use the <span class=\"symbol\">&#8211;directory<\/span> flag to serve files from a directory that is not necessarily the current directory.<\/p>\n<p>So the question now is, why would you ever need to write a script when you can just invoke the server easily from the terminal?<\/p>\n<p>Well, remember that you are using the\u00a0<strong>SimpleHTTPRequestHandler<\/strong>. If you want to create your custom handlers (which you will probably want to do) then you won&#8217;t be able to do that from the terminal.<\/p>\n<h3>Learning Python?<\/h3>\n<p>Check out <a href=\"https:\/\/courses.afternerd.com\/\">the Courses section!<\/a><\/p>\n\n<h2>Featured Posts<\/h2>\n<ul>\n<li><a href=\"https:\/\/www.afternerd.com\/blog\/learn-python\/\">The Python Learning Path (From Beginner to Mastery)<\/a><\/li>\n<li><a href=\"https:\/\/www.afternerd.com\/blog\/learn-computer-science\/\">Learn Computer Science (From Zero to Hero)<\/a><\/li>\n<li><a href=\"https:\/\/www.afternerd.com\/blog\/coding-interview\/\">Coding Interview Preparation Guide<\/a><\/li>\n<li><a href=\"https:\/\/www.afternerd.com\/blog\/stock-investing-for-beginners\/\">The Programmer&#8217;s Guide to Stock Market Investing<\/a><\/li>\n<li><a href=\"https:\/\/www.afternerd.com\/blog\/start-programming-blog\/\">How to Start Your Programming Blog?<\/a><\/li>\n<\/ul>\n<div class=\"after-post-box\">\n<h2>Are you Beginning your Programming Career?<\/h2>\n<h3>I provide my best content for beginners in the newsletter.<\/h3>\n<ul>\n<li>Python tips for beginners, intermediate, and advanced levels.<\/li>\n<li>CS Career tips and advice.<\/li>\n<li>Special discounts on my premium courses when they launch.<\/li>\n<\/ul>\n<p>And so much more&#8230;<\/p>\n<h3>Subscribe now. It&#8217;s Free.<\/h3>\n<p><script type=\"text\/javascript\" src=\"\/\/mautic.afternerd.com\/form\/generate.js?id=2\"><\/script><\/p>\n<\/div>\n\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":1015,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[13],"yst_prominent_words":[859,857,848,872,871,839,841,854,873,779,849,847,29,856,874,866,843,846,869,865],"_links":{"self":[{"href":"https:\/\/www.afternerd.com\/blog\/wp-json\/wp\/v2\/posts\/614"}],"collection":[{"href":"https:\/\/www.afternerd.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.afternerd.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.afternerd.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.afternerd.com\/blog\/wp-json\/wp\/v2\/comments?post=614"}],"version-history":[{"count":30,"href":"https:\/\/www.afternerd.com\/blog\/wp-json\/wp\/v2\/posts\/614\/revisions"}],"predecessor-version":[{"id":1331,"href":"https:\/\/www.afternerd.com\/blog\/wp-json\/wp\/v2\/posts\/614\/revisions\/1331"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.afternerd.com\/blog\/wp-json\/wp\/v2\/media\/1015"}],"wp:attachment":[{"href":"https:\/\/www.afternerd.com\/blog\/wp-json\/wp\/v2\/media?parent=614"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.afternerd.com\/blog\/wp-json\/wp\/v2\/categories?post=614"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.afternerd.com\/blog\/wp-json\/wp\/v2\/tags?post=614"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/www.afternerd.com\/blog\/wp-json\/wp\/v2\/yst_prominent_words?post=614"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}