cURL - command line tool and library for transferring data with URLs

mail

Inspecting HTTP response headers

Compare response codes to HTTP vs HTTPS hosts

This can be done with :
for webHost in www.example.com apache.org; do
	for scheme in http https; do
		url="${scheme}://$webHost"
		echo -e "$url"
		curl -sI "$url" | grep '^HTTP/1.1'
	done
	echo
done
http://www.example.com
HTTP/1.1 200 OK
https://www.example.com
HTTP/1.1 200 Connection established		different reason phrases

http://apache.org
HTTP/1.1 301 Moved Permanently
https://apache.org
HTTP/1.1 200 Connection established		different responses to HTTP / HTTPS requests
You may notice :
  • different status codes
    • websites administrators are free to serve content via HTTP or HTTPS or both
    • when both HTTP and HTTPS are supported, it is a common practice that a request to an HTTP resource is responded with an HTTP 301 code, redirecting the requester to the HTTPS version of that resource
  • different reason phrases
    • the reason phrase is optional in the response message (source)
    • reason phrases listed in the specification are only recommendations and can be replaced or removed (source)
    • reason phrases are made up by the HTTP server developers and can safely be ignored (source)
      HTTP/2 and HTTP/3 have no reason phrase

HTTP/1.1 headers vs HTTP/2 headers

  • HTTP status codes are not protocol-specific
  • there is no reason phrase in HTTP/2, but if we if we inspect response headers more thoroughly :
    for url in https://www.example.com https://www.google.com https://www.wikipedia.com https://nginx.org; do
    	echo -e "\n$url"
    	curl -sI "$url" | grep '^HTTP' | tr ' ' '_'	this is to highlight [SPACE] characters
    done
    https://www.example.com
    HTTP/1.1_200_Connection_established
    HTTP/2_200_
    
    https://www.google.com
    HTTP/1.1_200_Connection_established
    HTTP/2_204_
    
    https://www.wikipedia.com
    HTTP/1.1_200_Connection_established
    HTTP/2_301_
    
    https://nginx.org
    HTTP/1.1_200_Connection_established
    HTTP/1.1_200_OK
    We can see that :
    • cURL reports 2 HTTP status messages for each host :
      • one for HTTP/1.1
      • and one for HTTP/2. If not available, it shows a different version of the HTTP/1.1 status message
    • HTTP/2 status messages have a trailing [SPACE] since their reason phrases are empty strings
mail

cURL : how to ignore / bypass the current proxy settings ?

mail

How to check whether HTTP keep-alive is enabled ?

Request

  • curl -Iv http://www.example.com/ 2>&1 | grep -i 'connection #0'
  • curl -Iv -H 'Host: www.example.com' http://12.34.56.78/ 2>&1 | grep -i 'connection #0'

Answer

* Connection #0 to host www.example.com left intact
* Closing connection #0
the HTTP keep-alive is enabled
* Closing connection #0
the HTTP keep-alive is not enabled

Check

curl -Iv -H 'Host: www.example.com' http://12.34.56.78/ --next http://12.34.56.78/ 2>&1 | grep -i 'connection #0'
* Connection #0 to host www.example.com left intact
* Re-using existing connection! (#0) with www.example.com
* Connection #0 to host www.example.com left intact
mail

curl

Usage

cURL is a tool to transfer data to/from a server, using one of the supported protocols (HTTP, HTTPS, FTP, FTPS, GOPHER, DICT, TELNET, LDAP or FILE). It is designed to work without user interaction.
To play with HTTP/1.0 and HTTP/1.1, you can also use telnet.

Flags

Flag Protocol Usage
-A userAgent --user-agent userAgent Specify the user agent. This can also be done with : -H "User-Agent: userAgent"
-b file --cookie file Read saved cookies from file and send them to the HTTP server.
-c file --cookie-jar file Write received cookie(s) to file
-C --continue-at offset Continue / resume a file transfer at the given position : offset bytes will be skipped from the beginning of the source file.
Use -C - to tell cURL to automatically find out where/how to resume the transfer.
--compressed Request a compressed response using one of the algorithms libcurl supports, and return the uncompressed document.
This is equivalent to : -H "Accept-Encoding: gzip, deflate".
A server that does not support the requested compression method(s) will ignore the compress header request and simply reply back the contents uncompressed.
-D file --dump-header file
  • HTTP
  • FTP
-d data --data data HTTP Sends data in a POST request to the HTTP server, in a way that can emulate as if a user has filled in a HTML form and pressed the submit button. You MUST NOT urlencode this data.
-F formFieldName=formFieldValue HTTP Emulate a filled-in form in which a user has pressed the submit button. This causes cURL to POST data using the Content-Type multipart/form-data (allowing to upload binary data) according to RFC 1867. Use -F multiple times to submit several fields.
--ftp-pasv FTP Use the FTP passive mode.
--ftp-ssl FTP Use SSL/TLS for the FTP connection.
-H header
--header header
HTTP
  • provide extra header in the request
  • gateways/proxies/whatever on the line _may_ mangle/remove it
-I (capital i)
--head
  • HTTP
  • FTP
  • FILE
-i --include Include the HTTP headers in the output. This includes server name, date of the document, HTTP version, cookies and more...
Using both -i and -D is pleonastic as -i == -D /dev/stdout, but may collide with further options (like -o /dev/null). No big deal having an extra -i
-k --insecure Prevent the connection from failing when the SSL certificate check fails.
--limit-rate bytesPerSecond Limit the transfer speed (both for uploads and downloads) to bytesPerSecond bytes per second. bytesPerSecond can be appended a kKmMgG suffix for Kilo/Mega/Gigabytes per second.
-L --location
  • by default, cURL does not follow HTTP redirects (HTTP 3xx status code + Location HTTP header)
  • this flag instructs cURL to follow HTTP redirects
  • credentials are not re-sent.
--noproxy hosts Bypass the system proxy for the listed hosts
To match all hosts and actually disable the proxy, use the * wildcard within single quotes, otherwise it will be substituted by the shell :
--noproxy '*'
-o file --output file HTTP Write output to file instead of STDOUT
This allows fetching multiple files at once : curl -o http://www.example.com/foo.html -o http://www.example.com/bar.html
-q --disable If used as the first parameter on the command line, ignore the ~/.curlrc config file
-O --remote-name HTTP Write Output to a local file named like the remote file. This will write index.html locally :
curl -O http://www.example.com/index.html
cURL has no option to specify a destination directory, but it's possible to workaround this with : cd destinationDir; curl [options]; cd - (source)
--resolve host:port:ip.add.re.ss Pre-populates the DNS cache with entries for host:port pair so redirects and everything that operates against this pair will instead use the provided ip.add.re.ss
This looks like a good alternative to playing with HTTP headers during tests.
(sources : 1, 2)
-s --silent Silent mode. Don't show progress meter or error messages. Makes cURL mute.
--sslv2 -2
--sslv3 -3
HTTP Forces cURL to use SSL version 2 (respectively : 3) when negotiating with a remote SSL server.
Both are insecure and obsolete (Prohibiting SSL v2, Deprecating SSL v3) and must NOT be used anymore. Consider --tls directives instead.
HTTP
  • these options ask cURL to use the specified TLS version (or higher) when negotiating with a remote TLS server
  • in old versions of cURL, these forced cURL to use exactly the specified version (check the version number + )
  • TLS 1.3 is still pretty recent (august 2018) and not widely supported
-T file URL
--upload file URL
Upload file to URL. If no remote file name is given within URL, the local file name will be used. If URL doesn't end with a /, its last part will be considered as the remote file name.
-u login:password
--user login:password
HTTP
FTP
Specify login and password to use for server authentication
-U login:password
--proxy-user login:password
HTTP
FTP
Specify login and password to use for proxy authentication
-v Verbose mode
  • lines starting with > mean data sent by cURL"
  • < means data received by cURL that is hidden in normal cases
  • and lines starting with * mean additional info provided by cURL
-x proxy --proxy proxy Use the specified proxy. Format : protocol://proxyHost:proxyPort
-Xcommand
  • HTTP : specify a custom request method : POST, PUT, DELETE, ... Without -X, cURL defaults to GET.
  • FTP, SMTP and others : specify a custom command rather than the default one

Example

Basic examples :

return page content to stdout
curl http://www.example.com
return HTTP response headers + page content to stdout
  • curl -i http://www.example.com
  • curl -i http://www.example.com | less
same as above (HTTP headers + page content), but written to outputFile rather than stdout
curl -i -o outputFile http://www.example.com
This only outputs a progress meter to stdout :
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1270  100  1270    0     0  75107      0 --:--:-- --:--:-- --:--:-- 79375
same as above : HTTP headers + page content written to outputFile, without the progress meter
display the HTTP headers only (see also the wget method) :
  • curl -I http://www.example.com
  • curl -IXGET http://www.example.com
    Forces a GET HTTP request rather than the HEAD done with -I.
  • curl -iso /dev/null -D /dev/stdout http://www.example.com

Define an Akamai alias :

alias akcurl='curl -H "Pragma: akamai-x-get-cache-key, akamai-x-cache-on, akamai-x-cache-remote-on, akamai-x-get-true-cache-key, akamai-x-get-extracted-values, akamai-x-check-cacheable, akamai-x-get-request-id, akamai-x-serial-no, akamai-x-get-ssl-client-session-id"'

Testing Akamai :

curl -iso /dev/null -D /dev/stdout -H "Pragma: akamai-x-get-cache-key, akamai-x-cache-on, akamai-x-cache-remote-on, akamai-x-get-true-cache-key, akamai-x-get-extracted-values, akamai-x-check-cacheable, akamai-x-get-request-id, akamai-x-serial-no, akamai-x-get-ssl-client-session-id" -H "Host: www.example.com" http://12.34.56.78/

akcurl -iso /dev/null -D /dev/stdout -H "Host: www.example.com" http://12.34.56.78/

akcurl -iso /dev/null -D /dev/stdout -H "Host: old.example.com" http://example.com.edgesuite.net/

Log into WordPress back-office :

siteName='www.example.com'; login='username'; password='password'; baseUrl='http://'$siteName; resultFile='./index.html'; redirectTo='http%3A%2F%2F'$siteName'%2Fwp-admin%2F'; curl -L -iso $resultFile -D /dev/stdout --data "log=$login&pwd=$password&wp-submit=Se+connecter&redirect_to=$redirectTo&testcookie=1" --cookie ./cookie.txt $baseUrl/wp-login && grep --color=auto "Tableau de bord" $resultFile

Notes :
  • Even though ./cookie.txt isn't visible after executing this command, it won't work without specifying it (?)
  • So far, I've not been able to do the same with wget.
  • ./index.html contains both the HTTP headers and the page contents.
  • The form field names may change : log, password, wp-submit, as well as the submit button name : Se connecter.

Log into WordPress back-office with a custom HTTP header :

serverName='srvXYZ'; siteName='www.example.com'; login='username'; password='password'; baseUrl='http://'$serverName; resultFile='./index.html'; redirectTo='http%3A%2F%2F'$siteName'%2Fwp-admin%2F'; curl -L -iso $resultFile -D /dev/stdout --data "log=$login&pwd=$password&wp-submit=Connexion&redirect_to=$redirectTo&testcookie=1" --cookie ./cookie.txt --header "Host: $siteName" $baseUrl/wp-login.php

Notes, same as above, but also :
  • Don't forget to post data to wp-login.php, some redirects may not work (?)

Log into another back-office while sending custom HTTP host headers :

hostnameFake='my.productionSite.com'; hostnameReal='serverName'; resultFile='./result.txt'; cookieFile='./cookie.txt'; curl -iso $resultFile -D /dev/stdout --cookie-jar $cookieFile -H "Host: $hostnameFake" http://$hostnameReal/loginPage; curl -iso $resultFile -D /dev/stdout --data 'login=admin& password=secret' --cookie $cookieFile --cookie-jar $cookieFile -H "Host: $hostnameFake" http://$hostnameReal/loginCredentialsCheck; curl -iso $resultFile -D /dev/stdout --cookie $cookieFile -H "Host: $hostnameFake" http://$hostnameReal/contentPage; grep --color=auto "some text" $resultFile

Notes :
  • The data to be sent through HTTP POST must not be urlencoded
  • Albeit we're sending a tampered HTTP host header, no need to modify the cookie-jar file to translate the fake hostname into the real one. Just leave the cookie-jar file as-is.
  • Depending on the website, it may/may not be necessary to :
    • GET the login page first, for session cookies
    • specify the HTTP Referrer when presenting credentials

Get HTTP response times (source) :

Times :

  1. DNS lookup time : to look up the IP address of the domain in question
  2. Connect time : to set up the TCP connection
  3. Wait time : to receive the first byte after the connection has been setup aka TTFB
  4. Content download time

curl -s -o /dev/null -iD /dev/stdout -w "Effective URL :\t\t%{url_effective}\nContent-type :\t\t%{content_type}\nHTTP CODE :\t\t%{http_code}\nDNS lookup duration :\t%{time_namelookup} s\nConnect duration :\t%{time_connect} s\nTTFB :\t\t\t%{time_starttransfer} s\nTotal time :\t\t%{time_total} s\nDownloaded :\t\t%{size_download} bytes\n" http://www.example.com

Back to the origin + bypass host cache (Varnish) :

curl -s -o /dev/null -iD /dev/stdout -w "Effective URL :\t\t%{url_effective}\nContent-type :\t\t%{content_type}\nHTTP CODE :\t\t%{http_code}\nDNS lookup duration :\t%{time_namelookup} s\nConnect duration :\t%{time_connect} s\nTTFB :\t\t\t%{time_starttransfer} s\nTotal time :\t\t%{time_total} s\nDownloaded :\t\t%{size_download} bytes\n" --header "Host: www.example.com" http://origin-www.example.com?test=$RANDOM

Upload a file by FTP :

curl -u login:password -T localFileName ftp://ftpHost/path/to/upload/directory/remoteFileName

Connect to a secure FTP (about cURL certificates) :

curl --insecure --ftp-ssl --ftp-pasv --user "login:password" "ftp://ftpHost:ftpPort/path/to/upload/directory/"

The trailing / looks mandatory (?).