1. Hypertext Transfer Protocol. Based on RFC 822/MIME format.
  2. For transferring binary it uses base64 since HTTP is a protocol used to transfer text. To transfer binary it must be encoded as text and sent out. This is what base64 is used for. It encodes 4 characters per 3 bytes of data plus padding at the end. Thus, each 6 bits of input is encoded in a 64-character alphabet (efficiency is 4/3 = 1.333 times original).
  3. HTTP 1.1 added persistent connections, byte ranges, content negotiations, and cache support.
  4. Note that HTTP’s protocol overhead along with connection setup overhead of using TCP can make HTTP a poor choice for certain applications. In these cases, UDP is recommended (for example DNS uses simple UDP requests/responses for most of the DNS queries). Can mitigate some of the overhead by using persistent HTTP connections.

Basic format for requests/responses:

message = <start-line>

<start-line> = Request-Line | Status-Line
<message-header> = Field-Name ':' Field-Value

Request format:

Request-Line = Method SP(Space) URI SP(Space) HTTP-Version CRLF
Method = "OPTIONS"
       | "HEAD"
       | "GET"
       | "POST"
       | "PUT"
       | "DELETE"
       | "TRACE"
GET /articles/http-basics HTTP/1.1
Connection: keep-alive
Cache-Control: no-cache
Pragma: no-cache
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Response format:

Status-Line = HTTP-Version SP(Space) Status-Code SP(Space) Reason-Phrase CRLF
HTTP/1.1 200 OK
Server: nginx/1.6.1
Date: Tue, 02 Sep 2014 04:38:20 GMT
Content-Type: text/html
Last-Modified: Tue, 05 Aug 2014 11:18:35 GMT
Transfer-Encoding: chunked
Connection: keep-alive
Content-Encoding: gzip

Query String

Notes from: Wikipedia - Query string and’ RFC 3986 - Uniform Resource Identifier (URI).

  1. Part of the URL that does not really fit in a path hierarchy. Example:
  2. Server may pass the query string to a CGI (Common Gateway Interface) script.
  3. The ? separates the resource from the query string. Example for Google search: Thus, the URL can be bookmarked and shared.
  4. Usually used to store content of web forms.
  5. The format is key,value pairs. Series of pairs usually separated by &.
  6. The # is known as a fragment.
  \_/   \______________/\_________/ \_________/ \__/
   |           |            |            |        |
scheme     authority       path        query   fragment
   |   _____________________|__
  / \ /                        \

Chunked Transfer Encoding

  1. New feature of HTTP/1.1.
  2. According to RFC 2616: The chunked encoding modifies the body of a message in order to transfer it as a series of chunks, each with its own size indicator.
  3. Used to transfer dynamically produced content more efficiently.
  4. Uses Transfer-Encoding header instead of Content-Length header. Since there is no content length header, server can start sending response as it gets content.
  5. Size of each chunk is sent right before chunk so receiver knows when it has completed receiving chunks.
  6. Data transfer is terminated by chunk of length 0.
  7. Advantages, for example, is when response starts sending HTML page to browser (start with <head>, which includes external scripts location), so browser can start downloading this scripts in parallel.
  8. Sometimes you want to upload data but don’t know the length of data yet. A good use of this feature would be performing a database dump, piping the output to gzip, and then piping the gzip file directly to Cloud Files without writing the data to disk to compute the file size.
  9. Example is below. Note that the first chunk has size 0x45ea which is 17898 bytes. Then last chunk is 0 length.
C: GET / HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
User-Agent: HTTPie/0.8.0

S: HTTP/1.1 200 OK
Date: Thu, 16 Oct 2014 04:16:25 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
expires=Sat, 15-Oct-2016 04:16:25 GMT; path=/;
Set-Cookie: NID=67=PW5SAvG5XSS2ptSNeN6WfK11dy7qJxM3MM7sRvn_M3CPp6zdr_QihMyA66yTEt47n1PZyGHvIVv_9ecJW2-1LCwliBR1jzxj6F5fXDltgRWwbaTB9a7AFNHHw-qQ_V_g;
expires=Fri, 17-Apr-2015 04:16:25 GMT; path=/;; HttpOnly
P3P: CP="This is not a P3P policy! See
for more info."
Server: gws
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Alternate-Protocol: 80:quic,p=0.01
Transfer-Encoding: chunked

<!doctype html><html itemscope="" itemtype="" ...




Code Classification
1xx Informational
100 Continue
2xx Success
200 OK
3xx Redirection
301 Moved Permanently
302 Found
4xx Client Error
401 Unauthorized
403 Forbidden
404 Not Found
5xx Server Error
500 Internal Server Error


Code 301 Redirection

An example of this is when requesting a certain snapshot from the debian archives. Let’s request for a date (January 02, 2012 22:05:11) 20120102T220511Z:

$ http --headers get
HTTP/1.1 301 Moved Permanently
Accept-Ranges: bytes
Age: 0
Cache-Control: public, max-age=600
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 224
Content-Type: text/html; charset=UTF-8
Date: Wed, 01 Oct 2014 18:36:27 GMT
Expires: Wed, 01 Oct 2014 18:46:26 GMT
Server: Apache
Vary: Accept-Encoding
Via: 1.1 varnish
X-Varnish: 1485917301

Notice that we get back a 301 code that stands for redirection. We then get redirected to

Code 302 Found

Indicates resource resides temporarily under a different URI (10.3.3 302 Found).

$ http get
  HTTP/1.1 302 FOUND
  Connection: keep-alive
  Content-Language: en
  Content-Length: 0
  Content-Type: text/html; charset=utf-8
  Date: Tue, 14 Oct 2014 18:37:30 GMT
  Server: nginx/1.4.6 (Ubuntu)
  Vary: Accept-Language, Cookie
  X-Deity: chimera-lts
  X-Fallback: True



Fetch a resource. Example in python:

def get():
# Simple GET of index.html
headers = { 'User-Agent': 'http_client/0.1',
            'Accept': '*/*',
            'Accept-Encoding': 'gzip, deflate' }
http_conn = http.client.HTTPConnection("localhost")
http_conn.request("GET", "/", headers=headers)

## Response
resp = http_conn.getresponse()
print("Status:", resp.status, resp.reason)

## Cleanup

Difference Between POST and PUT

  1. POST is used for creating, PUT is used for updating (and creating). It’s also worthwhile to note that PUT should be idempotent whereas POST is not.

    • Idempotent means that same request over and over has same result. Thus, if you are doing PUT and connection dies, you can safely do a PUT again.
  2. Also, according to HTTP/1.1 spec:

    The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line

    The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.”

  3. Thus, POST can be used to create. PUT can be used to create or udpate.

  4. Difference is in terms of API calls. You usually do a POST to an API endpoint (or a URL that already exists).


  1. With PUT you actually create a valid path under the URL:
PUT /questions/<new_question> HTTP/1.1
  1. Thus, you use PUT to create the resource and then use that URL for POST.
  2. Note that with POST, server decides new URL path, with PUT user decides.

Persistent Connections

  1. Uses Connection: keep-alive header request/response header.
  2. Idea is to use single TCP connection to send and receive multiple HTTP Requests/Responses. Thus, avoiding expensive TCP handshake.
  3. This is default in HTTP/1.1.
  4. Disadvantages when single documents are repeatedly requested (e.g. images). This kills performance due to keeping unnecessary connections open for many seconds after document was retrieved.
  5. When you set up a TCP connection, you associate a set of timers. Some of the timers are used for keepalive.
  6. A Keepalive probe is a packet with no data and ACK flag turned on.
    • Note that in TCP/IP RFC, ACK segments with no data are not reliably transmitted by TCP. Thus, no retries.
    • Remote host doesn’t need to support keepalive. It will see an ACK packet and send back an ACK reply.
  7. Since TCP/IP is a stream oriented protocol, a zero length data packet is not dangerous for user program.
  8. If no reply packets are received for keepalive probe, can assume that connection is broken.
  9. Also useful when NAT terminates connection since it only can keep track of certain number of connections at a time.
  10. Useful to know if peers have died before notifying you (e.g. kernel panic, reboot).
 _____                                                     _____
|     |                                                   |     |
|  A  |                                                   |  B  |
|_____|                                                   |_____|
   ^                                                         ^
   |--->--->--->-------------- SYN -------------->--->--->---|
   |---<---<---<------------ SYN/ACK ------------<---<---<---|
   |--->--->--->-------------- ACK -------------->--->--->---|
   |                                                         |
   |                                       system crash ---> X
   |                                     system restart ---> ^
   |                                                         |
   |--->--->--->-------------- PSH -------------->--->--->---|
   |---<---<---<-------------- RST --------------<---<---<---|
   |                                                         |


  1. TCP Keepalive HOWTO
  2. Wikipedia - HTTP Persistent Connection
  3. RFC 1122 Section - TCP Keep-Alives

keepalive in Linux

Default is two hours before starting to send keepalive packets:

# cat /proc/sys/net/ipv4/tcp_keepalive_time

# cat /proc/sys/net/ipv4/tcp_keepalive_intvl

# cat /proc/sys/net/ipv4/tcp_keepalive_probes

To add support to your application use setsockopt() and configure the socket connection for keepalive.

Can also use libkeepalive with LD_PRELOAD to add support to any C application.

Document Caching

From: Google Browser Security Handbook, Part 2

  1. HTTP requests are expensive mainly because of overhead of setting up TCP connections. Thus, important to have the browser or intermediate system (proxy) maintain local copy of some of the data.

  2. The HTTP/1.0 spec did define some headers to handle caching but it did not provide any specific guidance.

    • Expires: This is a response header that allows server to declare an expiration date. When this date is passed, browsers must retrieve new document. There is a Date header as well which defines the date and time which message was originated. Sometimes, however, Date header is not part of response. Thus, implementation is then browser specific.

      The RFC also does not specify if the Expires is based on browser’s local clock. Thus, current practice is to compute Expires-Date delta and compare it to browser clock.

    • Pragma request header when set to no-cache permits clients to override intermediate systems to re-issue requests rather than retrieve cached data. For Pragma response header, it instructs browser not to cache this data.

    • Last-Modified response header indicates when resource was last updated according to server’s local clock. Reflects modification date of file system. Used in conjunction with If-Modified-Since request header to revalidate cache entries.

    • If-Modified-Since request header, permitting client to indicate what Last-Modified header it had seen on the version of the document already present in browser or proxy cache. If server calculates that no modification since If-Modified-Since date it returns 304 Not Modified response instead of requested document. Thus, client will redisplay cached content.

    • All of above was useful when content was static. Thus, with complex dynamic web apps, most developers turned off caching.

  3. HTTP/1.1 acknowledges the issue and establishes ground rules for what and when should be cached.

    • Only 200 (OK), 203 (Non-Authoritative), 206 (Partial Content), 300 (Multple Choices), and 301 (Redirection) responses are cacheable, and only if the method is not POST, PUT, DELETE, or TRACE.
    • Cache-Control header introduced that provides a fine-grained control over caching strategies.
      • no-cache disables cache all together. Can disable cache for certain specific headers as well (e.g. no-cache: Set-Cookie).
        • Firefox still stores responses because of back and forward navigation between sessions. But it doesn’t do this on https connections because of sensitive information such as banking, etc.
      • no-store: If in request don’t store any request response in cache. If sent in response, client must not store anything from request/response headers.
      • public/private: Controls caching on intermediate systems.
      • max-age: Time to live in seconds.


Basic Auth

This is the simplest form of authentication since it doesn’t require cookies, session identifier or login pages. It uses standard HTTP Authorization header to send login credentials. Thus, no handshakes need to be done.

Typically used over https since encoding is done in base64 (passwords sent as plain text). Passwords can be easily decoded.

On Server, status code 401 is sent back and the following header is used:

WWW-Authenticate: Basic realm="Restricted"

On Client, the Authorization header is used with the following format:

Authorization: Basic base64("username:password")

Example in python:

def get_auth():
# GET with authorization of index.html
authstring = base64.b64encode(("%s:%s" % ("amit","amit")).encode())
authheader = "Basic %s" % (authstring.decode())
print("Authorization: %s" % authheader)

headers = { 'User-Agent': 'http_client/0.1',
            'Accept': '*/*',
            'Authorization': authheader,
            'Accept-Encoding': 'gzip, deflate' }
http_conn = http.client.HTTPConnection("localhost")
http_conn.request("GET", "/", headers=headers)

## Response
resp = http_conn.getresponse()
print("Status:", resp.status, resp.reason)

## Cleanup


Basically uses MD5 of password and nonce value to prevent replay attacks. Now, pretty much replaced by HMAC (keyed-hash message authentication code).

A basic digest authentication session goes as follows:

  1. HTTP client performs a request (GET, POST, PUT, etc)

  2. HTTP server responds with a 401 error not authorized. In the response, a WWW-Authenticate header is sent that contains:

    • Digest algorithm - Usually MD5.
    • realm - The access realm. A string identifying the realm of the server.
    • qop - Stands for quality of protection (e.g. auth)
    • nonce - Server generated hash, issued only once per 401 response. Server should also have a timeout for the nonce values.
  3. Client then receives the 401 status error and parses the header so it knows how to authenticate itself. It responds with the usual header and adds an Authorization header containing:

    • Digest username
    • realm
    • nonce - Sends the server generated value back.
    • uri - Sends the path to the resource it is requesting.
    • algorithm - The algorithm the client used to compute the hashes.
    • qop
    • nc - hexadecimal counter for number of requests.
    • cnonce - client generated nonce, always is generated per request.
    • response - Computed hash of md5(HA1:nonce:nc:cnonce:qop:HA2).
      • HA1 = md5(username:realm:password)
      • HA2 = md5(<request method.:uri)

    Notice how the client does not send the password in plain text.

  4. Server computes hash and compares to client’s hash and if it matches sends back OK with content. Note that rspauth sent back by server is a mutual authentication proving to client it knows its secret.

  5. Note that each client needs to know the password and the password needs to be shared securely before hand.

Example HTTP Capture:

GET /files/ HTTP/1.1
Host: localhost
User-Agent: http_client/0.1
Accept-Encoding: gzip, deflate
Accept: */*

HTTP/1.1 401 Unauthorized
Server: nginx/1.6.1
Date: Sat, 06 Sep 2014 02:09:24 GMT
Content-Type: text/html
Content-Length: 194
Connection: keep-alive
WWW-Authenticate: Digest algorithm="MD5", qop="auth", realm="Access Restricted", nonce="2a27b9b6540a6cd4"

GET /files/ HTTP/1.1
Host: localhost
User-Agent: http_client/0.1
Accept-Encoding: gzip, deflate
Accept: */*
Authorization: Digest username="amit", realm="Access Restricted", nonce="2a27b9b6540a6cd4", uri="/files/",
response="421974c0c2805413b0d4187b9b143ecb", algorithm="MD5", qop="auth", nc=00000001, cnonce="e08190d5"

HTTP/1.1 200 OK
Server: nginx/1.6.1
Date: Sat, 06 Sep 2014 02:09:24 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Authentication-Info: qop="auth", rspauth="33fea6914ddcc2a25b03aaef5d6b478b", cnonce="e08190d5", nc=00000001..
Content-Encoding: gzip

Example Python Code:

def get_auth_digest():
    resp = get()

    # Get dictionary of headers
    headers = resp.getheader('WWW-Authenticate')
    h_list = [h.strip(' ') for h in headers.split(',')]
    #h_tuple = re.findall("(?P<name>.*?)=(?P<value>.*?)(?:,\s)", headers)
    h_tuple = [tuple(h.split('=')) for h in h_list]
    f = lambda x: x.strip('"')
    h = {k:f(v) for k,v in h_tuple}

    # HA1 = md5(username:realm:password)
    ha1_str = "%s:%s:%s" % ("amit",h['realm'],"amit")
    ha1 = hashlib.md5(ha1_str.encode()).hexdigest()

    # HA2 = md5(GET:uri) i.e. md5(GET:/files/)
    ha2_str = "%s:%s" % ('GET',path)
    ha2 = hashlib.md5(ha2_str.encode()).hexdigest()

    # Generate cnonce
    cnonce = hashlib.sha1(str(random.random()).encode()).hexdigest()[:8]

    # Generate response = md5(HA1:nonce:00000001:cnonce:qop:HA2)
    resp_str = "%s:%s:%s:%s:%s:%s" % (ha1,h['nonce'],"00000001",cnonce,h['qop'],ha2)
    resp_hash = hashlib.md5(resp_str.encode()).hexdigest()

    # Do another get
    authheader = 'Digest username="%s", realm="%s", nonce="%s", ' \
                 'uri="%s", response="%s", algorithm="%s", qop="%s", nc=00000001, ' \
                 'cnonce="%s"' \
                 % ("amit", h['realm'], h['nonce'], path, resp_hash, h['Digest algorithm'], h['qop'], cnonce)
    headers = { 'User-Agent': 'http_client/0.1',
                'Accept': '*/*',
                'Accept-Encoding': 'gzip, deflate',
                'Authorization': authheader

Certificate Based

Idea is to separate those who verify password (the server will have a copy or a hash of the password) and those who define the user identity. Thus, certificate authority (CA) issues a private certificate to a user, and guarantees that it can communicate using this key with the public key issued to the other business party.

Note that the downside becomes apparent when large number of clients or users need to authenticate to the server. Thus, CA needs to issue certificate for each user. These certificates needs to be verified and if one user is compromised the certificate of that user can be used to authenticate to the server unless the certificate is revoked.

For the reasons stated above, client authentication is rarely used with TLS. A common technique is to use TLS to authenticate the server to the client and to establish a private channel, and for the client to authenticate to the server using some other means - for example, a username and password using HTTP basic or digest authentication.


The above image depicts certificate-based authentication. The client asks the user to enter a password which unlocks the database holding the private key. The client then uses this private key to sign a random data and sends a certificate to the server. Thus, the password is never sent.

The Red Hat Portal discusses this in great detail.


  1. It’s HTTP over TLS or HTTP over SSL (https:// instead of http://). Thus, uses an added encryption layer (above Transport Layer, before Application Layer) of SSL/TLS to protect traffic.
  2. Main motivation is to prevent wiretapping/man in the middle attacks.
  3. HTTPS provides authentication of the website and associated web server that one is communicating with.
  4. Provides a bidirectional encryption of communications between a client and server.
  5. URL, query parameters, headers, are protected. However, it only protects HTTP layer. Thus, can infer host addresses, IP address, and sometimes port number of the webserver (since TCP layer is not encrypted). Can get data transferred and duration of TCP connection but not content.

Trusting a Web Site

  1. Web browsers know how to trust HTTPS websites based on certificate authorities that come pre-installed in their software.
  2. Certificate authorities, such as Comodo and GlobalSign, are in this way being trusted by web browser creators to provide valid certificates.
  3. Must use a browser that correctly implements HTTPS with correct pre-installed certificates.
  4. User trusts CA to “vouch” for website they issued certificate to.
  5. The user trusts that the protocol’s encryption layer (TLS/SSL) is sufficiently secure against eavesdroppers.


  1. Uses assymetric cryptography:
    • Basically known as public key cryptography.
    • Requires two keys. A private/secret and a public key.
    • Public key is used to encrypt plain text or verify a digital signature. Private key is used to decrypt the plain text or create a digital signature.
  2. The assymetric key is used for authentication and encrypting the channel. Then, a symmetric session key is exchanged.
  3. The session key is used to encrypt data flowing between the parties. Important property is forward secrecy. This means that the short-term session key cannot be derived from long term assymetric secret key.
  4. In OSI model equivalences, TLS/SSL is initialized at layer 5 (session layer) and works at layer 6 (the presentation layer). The session layer has a handshake using an asymmetric cipher in order to establish cipher settings and a shared key for that session; then the presentation layer encrypts the rest of the communication using a symmetric cipher and that session key.
  5. TLS is the new name for SSL.
  6. SSL got to version 3.0 and TLS is “SSL 3.1”.
  7. Current version of TLS is 1.2.
  8. SSL (secure socket layer) often refers to the old protocol variant which starts with the handshake right away and therefore requires another port for the encrypted protocol such as 443 instead of 80.
  9. TLS (transport layer security) often refers to the new variant which allows to start with an unencrypted traditional protocol and then issuing a command (usually STARTTLS) to initialize the handshake.
  10. Differences between SSL and TLS in the protocol level:
    • In the ClientHello message (first message sent by the client, to initiate the handshake), the version is {3,0} for SSLv3, {3,1} for TLSv1.0 and {3,2} for TLSv1.1.
    • The ClientKeyExchange differs.
    • The MAC/HMAC differs (TLS uses HMAC whereas SSL uses an earlier version of HMAC).
    • The key derivation differs.
    • The client can send application data can be sent straight after ending the SSL/TLS Finished message in SSLv3. In TLSv1, it must wait for the server’s Finished message.
    • The list of cipher suites differ (and some of them have been renamed from SSL_* to TLS_*, keeping the same id number).
    • There are also differences regarding the new re-negotiation extension.
  11. Use port 443 by default.
  12. TLS, which uses long-term public and secret keys to exchange a short term session key to encrypt the data flow between client and server.
  13. X.509 certificates are used to guarantee one is talking to the partner with whom one wants to talk.
  14. Need to ensure scripts are loaded over HTTPS as well and not HTTP.
  15. In case of compromised secret (private) key, certificate can be revoked.
  16. Use Perfect Forward Secrecy (PFS) so that short term session key can’t be derived from long term assymetric secret key.


Client                                               Server

ClientHello                  -------->
                             <--------      ServerHelloDone
Finished                     -------->
                             <--------             Finished
Application Data             <------->     Application Data
  1. Exchange hello messages to agree on algorithms, exchange random values, check for resume.
    • The ClientHello and ServerHello establish the following attributes: Protocol Version, Session ID, Cipher Suite, and Compression Method. Additionally, two random values are generated and exchanged: ClientHello.random and ServerHello.random.
  2. Exchange necessary crypto parameters for client/server to agree on premaster secret.
  3. Exchange certs and crypto information to allow client/server to authenticate.
  4. Generate a master secret from the premaster secret and exchanged random values.
  5. Provide security parameters to the record layer.
  6. Allow the client and server to verify that their peer has calculated the same security parameters and that the handshake occurred without tampering by an attacker.

Server Setup

  1. To prepare a web server to accept HTTPS connections, the administrator must create a public key certificate for the web server.
  2. This certificate must be signed by a trusted certificate authority for the web browser to accept it without warning.
  3. Web browsers are generally distributed with a list of signing certificates of major certificate authorities so that they can verify certificates signed by them.

Other Uses

  1. The system can also be used for client authentication in order to limit access to a web server to authorized users. To do this, the site administrator typically creates a certificate for each user, a certificate that is loaded into his/her browser.

nginx engineX


Make sure the permissions of the files in the directory are accessible to the other group. Or change the permissions to the user that nginx runs as (for debian it’s www-data).

Setting up Basic Auth

  1. Install apache2-utils to get htpasswd
  2. Create an .htpasswd file in the web root. Make sure the permissions are 644. Note that the password generated by htpasswd is an apache modified version of MD5.
sudo htpasswd -c /usr/share/nginx/html/.htpasswd amit
  1. Update /etc/nginx/sites-available/default in the location / and reload nginx:
# Basic auth
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;

Setting up Digest Auth

  1. apache2-utils includes htdigest (similar to htpasswd) to generate digest key.
  2. Create an .htdigest file in the web root. Make sure the permissions are 644. Note that the realm here is “Access Restricted”.
sudo htdigest -c /usr/share/nginx/html/.htdigest "Access Restricted" amit
  1. Need to build with nginx-http-auth-digest module from In order to do this, download nginx debian sources, copy nginx-http-auth-digest to debian/modules, and finally edit debian/rules to build nginx-http-auth-digest (look at –add-module config option).
  2. Update /etc/nginx/sites-available/default in the location / and reload nginx:
# Digest auth
auth_digest "Access Restricted";    # Realm
auth_digest_user_file /usr/share/nginx/html/.htdigest;


HTTPie - Command Line HTTP Client

Very useful and feature rich command line http client written in Python (

Useful for debugging HTTP requests. For example:

$ http get http://localhost
HTTP/1.1 200 OK
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html
Date: Mon, 01 Sep 2014 18:31:03 GMT
Last-Modified: Tue, 05 Aug 2014 11:18:35 GMT
Server: nginx/1.6.1
Transfer-Encoding: chunked

<!DOCTYPE html>
<title>Welcome to nginx!</title>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href=""></a>.<br/>
Commercial support is available at
<a href=""></a>.</p>

<p><em>Thank you for using nginx.</em></p>