diff --git a/CHANGELOG.md b/CHANGELOG.md
index 73f285c97db..eaf466f00bd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 - The `WithSchemaURL` option function in `go.opentelemetry.io/contrib/bridges/otelslog`.
   This option function is used as a replacement of `WithInstrumentationScope` to specify the semantic convention schema URL for the logged records. (#5588)
 - Add support for Cloud Run jobs in `go.opentelemetry.io/contrib/detectors/gcp`. (#5559)
+- Add TLS information from semantic conventions v1.24.0 to `go.opentelemetry.io/contrib/instrumentation/net/http/httptrace`. (#5563)
 
 ### Changed
 
diff --git a/instrumentation/net/http/httptrace/otelhttptrace/clienttrace.go b/instrumentation/net/http/httptrace/otelhttptrace/clienttrace.go
index 67e03f24810..353b9b33225 100644
--- a/instrumentation/net/http/httptrace/otelhttptrace/clienttrace.go
+++ b/instrumentation/net/http/httptrace/otelhttptrace/clienttrace.go
@@ -5,11 +5,15 @@ package otelhttptrace // import "go.opentelemetry.io/contrib/instrumentation/net
 
 import (
 	"context"
+	"crypto/sha256"
 	"crypto/tls"
+	"encoding/base64"
+	"fmt"
 	"net/http/httptrace"
 	"net/textproto"
 	"strings"
 	"sync"
+	"time"
 
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
@@ -36,6 +40,19 @@ var (
 	HTTPDNSAddrs               = attribute.Key("http.dns.addrs")
 )
 
+// TLS attributes.
+//
+// From https://opentelemetry.io/docs/specs/semconv/attributes-registry/tls/ but only present from semconv v1.24.
+var (
+	TLSCipherKey                 = attribute.Key("tls.cipher")
+	TLSProtocolVersionKey        = attribute.Key("tls.protocol.version")
+	TLSResumedKey                = attribute.Key("tls.resumed")
+	TLSServerCertificateChainKey = attribute.Key("tls.server.certificate_chain")
+	TLSServerHashSha256Key       = attribute.Key("tls.server.hash.sha256")
+	TLSServerNotAfterKey         = attribute.Key("tls.server.not_after")
+	TLSServerNotBeforeKey        = attribute.Key("tls.server.not_before")
+)
+
 var hookMap = map[string]string{
 	"http.dns":     "http.getconn",
 	"http.connect": "http.getconn",
@@ -316,8 +333,29 @@ func (ct *clientTracer) tlsHandshakeStart() {
 	ct.start("http.tls", "http.tls")
 }
 
-func (ct *clientTracer) tlsHandshakeDone(_ tls.ConnectionState, err error) {
-	ct.end("http.tls", err)
+func (ct *clientTracer) tlsHandshakeDone(state tls.ConnectionState, err error) {
+	attrs := make([]attribute.KeyValue, 0, 7)
+	attrs = append(attrs,
+		TLSCipherKey.String(tls.CipherSuiteName(state.CipherSuite)),
+		TLSProtocolVersionKey.String(tls.VersionName(state.Version)),
+		TLSResumedKey.Bool(state.DidResume),
+	)
+
+	if len(state.PeerCertificates) > 0 {
+		certChain := make([]string, len(state.PeerCertificates))
+		for i, cert := range state.PeerCertificates {
+			certChain[i] = base64.StdEncoding.EncodeToString(cert.Raw)
+		}
+
+		leafCert := state.PeerCertificates[0]
+		attrs = append(attrs,
+			TLSServerCertificateChainKey.StringSlice(certChain),
+			TLSServerHashSha256Key.String(fmt.Sprintf("%X", sha256.Sum256(leafCert.Raw))),
+			TLSServerNotAfterKey.String(leafCert.NotAfter.UTC().Format(time.RFC3339)),
+			TLSServerNotBeforeKey.String(leafCert.NotBefore.UTC().Format(time.RFC3339)),
+		)
+	}
+	ct.end("http.tls", err, attrs...)
 }
 
 func (ct *clientTracer) wroteHeaderField(k string, v []string) {
diff --git a/instrumentation/net/http/httptrace/otelhttptrace/httptrace_test.go b/instrumentation/net/http/httptrace/otelhttptrace/httptrace_test.go
index faf81b85640..bd04f9c6aae 100644
--- a/instrumentation/net/http/httptrace/otelhttptrace/httptrace_test.go
+++ b/instrumentation/net/http/httptrace/otelhttptrace/httptrace_test.go
@@ -5,6 +5,7 @@ package otelhttptrace_test
 
 import (
 	"context"
+	"net"
 	"net/http"
 	"net/http/httptest"
 	"strings"
@@ -69,16 +70,16 @@ func TestRoundtrip(t *testing.T) {
 	defer ts.Close()
 
 	address := ts.Listener.Addr()
-	hp := strings.Split(address.String(), ":")
+	host, port, _ := net.SplitHostPort(address.String())
 	expectedAttrs = map[attribute.Key]string{
-		semconv.NetHostNameKey:              hp[0],
-		semconv.NetHostPortKey:              hp[1],
+		semconv.NetHostNameKey:              host,
+		semconv.NetHostPortKey:              port,
 		semconv.NetProtocolVersionKey:       "1.1",
 		semconv.HTTPMethodKey:               "GET",
 		semconv.HTTPSchemeKey:               "http",
 		semconv.HTTPTargetKey:               "/",
 		semconv.HTTPRequestContentLengthKey: "3",
-		semconv.NetSockPeerAddrKey:          hp[0],
+		semconv.NetSockPeerAddrKey:          host,
 		semconv.NetTransportKey:             "ip_tcp",
 		semconv.UserAgentOriginalKey:        "Go-http-client/1.1",
 	}
diff --git a/instrumentation/net/http/httptrace/otelhttptrace/test/clienttrace_test.go b/instrumentation/net/http/httptrace/otelhttptrace/test/clienttrace_test.go
index 846f4fd128f..4d8b65e7b51 100644
--- a/instrumentation/net/http/httptrace/otelhttptrace/test/clienttrace_test.go
+++ b/instrumentation/net/http/httptrace/otelhttptrace/test/clienttrace_test.go
@@ -6,9 +6,14 @@ package test
 import (
 	"bytes"
 	"context"
+	"crypto/sha256"
+	"encoding/base64"
+	"fmt"
 	"net/http"
 	"net/http/httptest"
 	"net/http/httptrace"
+	"net/url"
+	"slices"
 	"testing"
 	"time"
 
@@ -42,107 +47,153 @@ func getSpansFromRecorder(sr *tracetest.SpanRecorder, name string) []trace.ReadO
 }
 
 func TestHTTPRequestWithClientTrace(t *testing.T) {
-	sr := tracetest.NewSpanRecorder()
-	tp := trace.NewTracerProvider(trace.WithSpanProcessor(sr))
-	otel.SetTracerProvider(tp)
-	tr := tp.Tracer("httptrace/client")
-
-	// Mock http server
-	ts := httptest.NewServer(
+	// Mock http server, one without TLS and another with TLS.
+	tsHTTP := httptest.NewServer(
 		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		}),
 	)
-	defer ts.Close()
-	address := ts.Listener.Addr()
+	defer tsHTTP.Close()
 
-	client := ts.Client()
-	err := func(ctx context.Context) error {
-		ctx, span := tr.Start(ctx, "test")
-		defer span.End()
-		req, _ := http.NewRequest("GET", ts.URL, nil)
-		_, req = otelhttptrace.W3C(ctx, req)
+	tsHTTPS := httptest.NewTLSServer(
+		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		}),
+	)
+	defer tsHTTPS.Close()
+
+	for _, ts := range []*httptest.Server{tsHTTP, tsHTTPS} {
+		sr := tracetest.NewSpanRecorder()
+		tp := trace.NewTracerProvider(trace.WithSpanProcessor(sr))
+		otel.SetTracerProvider(tp)
+		tr := tp.Tracer("httptrace/client")
+
+		err := func(ctx context.Context) error {
+			ctx, span := tr.Start(ctx, "test")
+			defer span.End()
+			req, _ := http.NewRequest("GET", ts.URL, nil)
+			_, req = otelhttptrace.W3C(ctx, req)
+
+			res, err := ts.Client().Do(req)
+			if err != nil {
+				t.Fatalf("Request failed: %s", err.Error())
+			}
+			_ = res.Body.Close()
 
-		res, err := client.Do(req)
+			return nil
+		}(context.Background())
 		if err != nil {
-			t.Fatalf("Request failed: %s", err.Error())
+			panic("unexpected error in http request: " + err.Error())
 		}
-		_ = res.Body.Close()
 
-		return nil
-	}(context.Background())
-	if err != nil {
-		panic("unexpected error in http request: " + err.Error())
-	}
+		type tc struct {
+			name       string
+			attributes []attribute.KeyValue
+			parent     string
+		}
 
-	testLen := []struct {
-		name       string
-		attributes []attribute.KeyValue
-		parent     string
-	}{
-		{
-			name: "http.connect",
-			attributes: []attribute.KeyValue{
-				attribute.Key("http.conn.done.addr").String(address.String()),
-				attribute.Key("http.conn.done.network").String("tcp"),
-				attribute.Key("http.conn.start.network").String("tcp"),
-				attribute.Key("http.remote").String(address.String()),
+		testLen := []tc{
+			{
+				name: "http.connect",
+				attributes: []attribute.KeyValue{
+					attribute.Key("http.conn.done.addr").String(ts.Listener.Addr().String()),
+					attribute.Key("http.conn.done.network").String("tcp"),
+					attribute.Key("http.conn.start.network").String("tcp"),
+					attribute.Key("http.remote").String(ts.Listener.Addr().String()),
+				},
+				parent: "http.getconn",
 			},
-			parent: "http.getconn",
-		},
-		{
-			name: "http.getconn",
-			attributes: []attribute.KeyValue{
-				attribute.Key("http.remote").String(address.String()),
-				attribute.Key("net.host.name").String(address.String()),
-				attribute.Key("http.conn.reused").Bool(false),
-				attribute.Key("http.conn.wasidle").Bool(false),
+			{
+				name: "http.getconn",
+				attributes: []attribute.KeyValue{
+					attribute.Key("http.remote").String(ts.Listener.Addr().String()),
+					attribute.Key("net.host.name").String(ts.Listener.Addr().String()),
+					attribute.Key("http.conn.reused").Bool(false),
+					attribute.Key("http.conn.wasidle").Bool(false),
+				},
+				parent: "test",
+			},
+			{
+				name:   "http.receive",
+				parent: "test",
+			},
+			{
+				name:   "http.headers",
+				parent: "test",
+			},
+			{
+				name:   "http.send",
+				parent: "test",
+			},
+			{
+				name: "test",
 			},
-			parent: "test",
-		},
-		{
-			name:   "http.receive",
-			parent: "test",
-		},
-		{
-			name:   "http.headers",
-			parent: "test",
-		},
-		{
-			name:   "http.send",
-			parent: "test",
-		},
-		{
-			name: "test",
-		},
-	}
-	for _, tl := range testLen {
-		span, ok := getSpanFromRecorder(sr, tl.name)
-		if !assert.True(t, ok) {
-			continue
 		}
 
-		if tl.parent != "" {
-			parent, ok := getSpanFromRecorder(sr, tl.parent)
-			if assert.True(t, ok) {
-				assert.Equal(t, span.Parent().SpanID(), parent.SpanContext().SpanID())
-			}
+		u, err := url.Parse(ts.URL)
+		if err != nil {
+			panic("unexpected error in parsing httptest server URL: " + err.Error())
+		}
+		// http.tls only exists on HTTPS connections.
+		if u.Scheme == "https" {
+			testLen = append([]tc{{
+				name: "http.tls",
+				attributes: []attribute.KeyValue{
+					attribute.Key("tls.server.certificate_chain").StringSlice(
+						[]string{base64.StdEncoding.EncodeToString(ts.Certificate().Raw)},
+					),
+					attribute.Key("tls.server.hash.sha256").
+						String(fmt.Sprintf("%X", sha256.Sum256(ts.Certificate().Raw))),
+					attribute.Key("tls.server.not_after").
+						String(ts.Certificate().NotAfter.UTC().Format(time.RFC3339)),
+					attribute.Key("tls.server.not_before").
+						String(ts.Certificate().NotBefore.UTC().Format(time.RFC3339)),
+				},
+				parent: "http.getconn",
+			}}, testLen...)
 		}
-		if len(tl.attributes) > 0 {
-			attrs := span.Attributes()
-			if tl.name == "http.getconn" {
-				// http.local attribute uses a non-deterministic port.
-				local := attribute.Key("http.local")
-				var contains bool
-				for i, a := range attrs {
-					if a.Key == local {
-						attrs = append(attrs[:i], attrs[i+1:]...)
-						contains = true
-						break
+
+		for i, tl := range testLen {
+			span, ok := getSpanFromRecorder(sr, tl.name)
+			if !assert.True(t, ok) {
+				continue
+			}
+
+			if tl.parent != "" {
+				parent, ok := getSpanFromRecorder(sr, tl.parent)
+				if assert.True(t, ok) {
+					assert.Equal(t, span.Parent().SpanID(), parent.SpanContext().SpanID())
+				}
+			}
+			if len(tl.attributes) > 0 {
+				attrs := span.Attributes()
+				if tl.name == "http.getconn" {
+					// http.local attribute uses a non-deterministic port.
+					local := attribute.Key("http.local")
+					var contains bool
+					for i, a := range attrs {
+						if a.Key == local {
+							attrs = append(attrs[:i], attrs[i+1:]...)
+							contains = true
+							break
+						}
+					}
+					assert.True(t, contains, "missing http.local attribute")
+				}
+				if tl.name == "http.tls" {
+					if i == 0 {
+						tl.attributes = append(tl.attributes, attribute.Key("tls.resumed").Bool(false))
+					} else {
+						tl.attributes = append(tl.attributes, attribute.Key("tls.resumed").Bool(true))
 					}
+					attrs = slices.DeleteFunc(attrs, func(a attribute.KeyValue) bool {
+						// Skip keys that are unable to be detected beforehand.
+						if a.Key == otelhttptrace.TLSCipherKey || a.Key == otelhttptrace.TLSProtocolVersionKey {
+							return true
+						}
+						return false
+					})
 				}
-				assert.True(t, contains, "missing http.local attribute")
+				assert.ElementsMatch(t, tl.attributes, attrs)
 			}
-			assert.ElementsMatch(t, tl.attributes, attrs)
 		}
 	}
 }