#!/usr/bin/env bash # # Shows time in seconds to first byte of a url or urls # # Based on a gist https://gist.github.com/sandeepraju/1f5fbdbdd89551ba7925abe2645f92b5 # by https://github.com/sandeepraju # # Modified by jay@gooby.org, @jaygooby # # Usage: ttfb [options] url [url...] # -d debug # -l (infers -d) log response headers. Defaults to ./curl.log # -n of times to test time to first byte # -v verbose output. Show request breakdown during -n calls # # Examples: # # ttfb https://example.com/example/url # .098974 # # ttfb -n 5 https://example.com/ # ..... # fastest .099195 slowest .103138 median .099684 # # ttfb -n 5 bbc.co.uk news.bbc.co.uk # ..... # ..... # bbc.co.uk fastest .045873 slowest .046870 median .045999 # news.bbc.co.uk fastest .042286 slowest .060245 median .046035 # # ttfb bbc.co.uk news.bbc.co.uk # bbc.co.uk .048378 # news.bbc.co.uk .049303 # # Implicitly follows redirects using curl's -L option. # # Log all response headers (default log file is ./curl.log) by calling with -d # # Override the default log file by specifying -l /some/file # # Get min, max and median values by specifying the number of times to call # the URL; use -n2 for 2 tests, -n5 for 5 and so on. # # If you specify more than one url and have specified -d or -l, the log file # will be prefixed with the URL being requested. # # If you specify -n and -d or -l, the response headers from the consecutive # requests will be concatenated in the log file. # # See https://blog.cloudflare.com/a-question-of-timing/ # and https://curl.haxx.se/docs/manpage.html for an explanation # of how the curl variables relate to the various stages of # the transfer. # # To get a better approximation of devtool's TTFB, consider # the time without the connection overhead: # %{time_starttransfer} - %{time_appconnect} # # Uses a dirty eval to do the ttfb arithmetic. Depends # on bc and column commands. set -eu # check dependencies for dependency in curl bc column; do which $dependency > /dev/null || (echo "You need to have '$dependency' installed and in your \$PATH" >&2 && exit 1) done # Ensure curl uses period separators for floating point values, which # bc requires to do calculations (i.e. it can't use locale separators like ,) if (locale -a | egrep ^C.UTF-8$ > /dev/null); then export LC_ALL=C.UTF-8 else export LC_ALL=C fi # check curl can use http2 HTTP_VERSION="--http2" curl -so /dev/null --http2 https://example.com || HTTP_VERSION="--http1.1" # Cribbed from https://stackoverflow.com/a/41762669/391826 median() { arr=($(printf '%s\n' "${@}" | sort -n)) nel=${#arr[@]} if (( $nel % 2 == 1 )); then # Odd number of elements val="${arr[ $(($nel/2)) ]}" else # Even number of elements (( j=nel/2 )) (( k=j-1 )) val=$(echo "scale=6;(${arr[j]}" + "${arr[k]})"/2|bc -l) fi echo $val } show_usage() { echo -e "Usage: ttfb [options] url [url...]\n\t-d debug\n\t-l (infers -d) log response headers. Defaults to ./curl.log\n\t-n of times to test time to first byte\n\t-o