cURL to Go (net/http) Converter

Paste a curl command and get a runnable package main program using only Go's standard library. The command is converted locally, so any keys or cookies in it stay on your computer.

cURL → Go net/http

Input

Settings

History

Load from URL

A runnable Go program from one curl command

Go developers often receive an API call as a curl command and need the net/http equivalent, which is verbose: build a request, set headers, configure a client, handle every error, read and close the body. This converter writes all of that for you as a complete program with package main, a main function and only standard-library imports. Save it as main.go and go run it, or lift the request-building lines into your own code.

The layout matches gofmt exactly — tabs, sorted imports, aligned struct fields — so your editor will not reformat it.

Request construction

  • The request is created with http.NewRequest, using constants such as http.MethodPost for standard verbs and a string for custom ones.
  • A -d or --json body is wrapped in strings.NewReader, using a raw string literal (backticks) when the body allows it, so JSON stays readable.
  • -F fields are written with mime/multipart: writer.WriteField for text and a small addFile helper that streams a file from disk with os.Open and CreateFormFile. The Content-Type header then comes from writer.FormDataContentType(), which includes the boundary.
  • -T file uploads the opened file as the body.
  • Each -H becomes req.Header.Set; headers that repeat use req.Header.Add so every value is sent. A Host header is assigned to req.Host, because Go ignores it in the header map.
  • -u user:pass becomes req.SetBasicAuth.

Client and transport settings

Options that affect the connection are set on http.Client and its http.Transport:

  • -m 2.5 → Timeout: 2500 * time.Millisecond (whole seconds use time.Second).
  • --connect-timeout → a net.Dialer with its own timeout in DialContext.
  • -k → TLSClientConfig: &tls.Config{InsecureSkipVerify: true}.
  • -x → Proxy: http.ProxyURL(proxyURL), with -U credentials attached through url.UserPassword.
  • No -L → a CheckRedirect function returning http.ErrUseLastResponse, because Go’s client follows up to ten redirects by default while curl follows none.

--compressed needs no code: Go’s transport already requests and transparently decompresses gzip, as an info note explains. Errors are handled with log.Fatal, and the program prints resp.Status and the body.

Limits and privacy

Per-part content types and custom file names given with -F '[email protected];type=image/png' are not reproduced by the addFile helper, and a note says so. Options that only change curl’s own output are skipped; anything with behaviour the code does not reproduce is listed as a warning or info message. The command, including any secrets, is processed entirely in your browser. Format other Go code with the Go formatter.

If the response is JSON, the next step is usually decoding it into a struct: paste a sample response into JSON to Go struct and swap io.ReadAll for json.NewDecoder(resp.Body).Decode(&v).

Examples

PUT JSON with a timeout and -k

The JSON body uses a raw string literal, the client gets a 2500 ms timeout and a transport that skips TLS verification.

Input
curl -X PUT https://staging.example.com/api/items/42 \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer sk_test_4eC39HqLyjWDarjtT1zdp7dc' \
  -d '{"name":"Mechanical keyboard","price":129.9}' \
  -k --max-time 2.5
Output
package main

import (
	"crypto/tls"
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
	"time"
)

func main() {
	body := strings.NewReader(`{"name":"Mechanical keyboard","price":129.9}`)
	req, err := http.NewRequest(http.MethodPut, "https://staging.example.com/api/items/42", body)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer sk_test_4eC39HqLyjWDarjtT1zdp7dc")

	client := &http.Client{
		Timeout: 2500 * time.Millisecond,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		},
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			// curl follows redirects only with -L
			return http.ErrUseLastResponse
		},
	}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(resp.Status)
	fmt.Println(string(respBody))
}
Open this example in the tool

Multipart upload through a proxy

Builds a multipart body with an addFile helper, configures an authenticated proxy, and omits CheckRedirect because -L is present.

Input
curl https://files.example.com/upload \
  -F '[email protected]' -F 'user=aisha' \
  -x http://proxy.corp:3128 -U svc:hunter2 -L
Output
package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
)

func main() {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	if err := addFile(writer, "avatar", "me.png"); err != nil {
		log.Fatal(err)
	}
	if err := writer.WriteField("user", "aisha"); err != nil {
		log.Fatal(err)
	}
	if err := writer.Close(); err != nil {
		log.Fatal(err)
	}
	req, err := http.NewRequest(http.MethodPost, "https://files.example.com/upload", body)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())

	proxyURL, err := url.Parse("http://proxy.corp:3128")
	if err != nil {
		log.Fatal(err)
	}
	proxyURL.User = url.UserPassword("svc", "hunter2")

	client := &http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyURL(proxyURL),
		},
	}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(resp.Status)
	fmt.Println(string(respBody))
}

// addFile copies a file from disk into a multipart form field.
func addFile(writer *multipart.Writer, field, path string) error {
	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer file.Close()
	part, err := writer.CreateFormFile(field, filepath.Base(path))
	if err != nil {
		return err
	}
	_, err = io.Copy(part, file)
	return err
}
Open this example in the tool

Common errors and how to fix them

ErrorCauseFix
The file "me.png" for form field "avatar" is not available here; the code uploads it from disk or contains a placeholderA warning: file uploads are read from disk when the Go program runs, not during conversion.Run the program from a directory where the path resolves, or change it to an absolute path.
A GET request with a body is unusual; many clients (including fetch) refuse to send it. Did you mean -G?The command sets -X GET together with -d data.Add -G to send the data as a query string, or change the method.
The upload file "backup.tar" (-T) cannot be read here; the code uploads it from disk or contains a placeholderThe command uploads a file with -T.Nothing to change if the path is right; the generated code opens the file with os.Open.
Only the first command is converted; everything from "&" onwards is ignoredThe pasted text chains several commands.Convert each curl command separately.

Frequently asked questions

Does the generated code need third-party packages?

No. It uses only the standard library: net/http, plus packages such as mime/multipart, crypto/tls or net/url when the command needs them.

Why is there a CheckRedirect function?

Go follows redirects by default, but your curl command does not use -L. Returning http.ErrUseLastResponse makes the client stop at the first response, as curl would.

Will gofmt change the output?

No. Imports are sorted, indentation uses tabs and composite-literal fields are aligned the way gofmt prints them.

Is the curl command uploaded anywhere?

No. It is parsed by JavaScript running in this page, and the Go code is generated on your device.

Related tools