This short tutorial explains how to replay the captured browser’s network offline using Python. Once you have captured the browser’s network as a HAR file, you can set up a proxy server in order to provide the responses for certain requests which exist in the HAR. Let’s start with creating a dictionary from the HAR. json dict_url_response = {} fob = open( , ) data = json.load(fob) fob.close() entries = data[ ][ ] entry entries: url = entry[ ][ ] response = entry[ ] dict_url_response[url] = response import "har/cap1.har" "r" "log" "entries" for in "request" "url" "response" Now create a class which we will be using for interception in the proxy. mitmproxy.net.http Response, Headers : url = flow.request.url har_response = dict_url_response[url] text = har_response[ ][ ] byt = str.encode(text) list_headers = [] obj har_response[ ]: list_headers.append((str.encode(obj[ ]), str.encode(obj[ ]))) headers = Headers(list_headers) response = Response( http_version = str.encode(har_response[ ]), status_code = har_response[ ], reason = str.encode(har_response[ ]), headers = headers, content = byt, trailers = , timestamp_start = , timestamp_end = ) KeyError: print( ) headers = Headers([ ( , ), ]) response = Response( http_version = str.encode( ), status_code = , reason = str.encode( ), headers = headers, content = str.encode( ), trailers = , timestamp_start = , timestamp_end = ) flow.response = response from import : class Interception : def request (self, flow) try "content" "text" for in "headers" "name" "value" "httpVersion" "status" "statusText" None 0. 1. except "Request URL not found in HAR" b'Content-Type' b'text/plain; charset=utf-8' "http/2.0" 200 "OK" "Request URL not found in HAR\n" None 0. 1. Finally, we just have to start the MITM proxy with the interception. mitmproxy.options Options mitmproxy.proxy.config ProxyConfig mitmproxy.proxy.server ProxyServer mitmproxy.tools.dump DumpMaster threading asyncio intercept = Interception() asyncio.set_event_loop(loop) m.run_loop(loop.run_forever) options = Options( listen_host= , listen_port= , http2= ) m = DumpMaster( options, with_termlog= , with_dumper= ) m.addons.add(intercept) config = ProxyConfig(options) m.server = ProxyServer(config) loop = asyncio.get_event_loop() t = threading.Thread( target=thread_func, args=(loop, m) ) t.start() from import from import from import from import import import : def thread_func (loop, m) '0.0.0.0' 8080 True True True The is used in order to keep the proxy server running. The proxy server listens on . thread_func 127.0.0.1:8080 In order to test the HAR replay you have to make the browser go through the proxy server, e.g. for Chrome the command would be: /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --proxy-server=127.0.0.1:8080 Sometimes the requests that are captured in the HAR are gzip or brotli compressed. In this GitHub repository such cases are taken into account: https://github.com/endgame-is-on/HAReplay Also published on Medium's endgame-is-on