Repository: avenga/couper
Pull request #578 (2022-09-07 15:11:37)

More hclsyntax
Simplify code by using only hclsyntax bodies

---
<details>
    <summary>Reviewer checklist</summary>
    <ul>
        <li>Read PR description: a summary about the changes is required</li>
        <li>Changelog updated</li>
        <li>Documentation: docs/{Reference, Cli, ...}, Docker and cli help/usage</li>
        <li>Pulled branch, manually tested</li>
        <li>Verified requirements are met</li>
        <li>Reviewed the code</li>
        <li>Reviewed the related tests</li>
    </ul>
</details>



Patch:
diff --git a/backend/backend_context.go b/backend/backend_context.go
index 6fa9250c..fecfe6c4 100644
--- a/backend/backend_context.go
+++ b/backend/backend_context.go
@@ -5,17 +5,17 @@ import (
 	"net/http"
 
 	"github.com/avenga/couper/config/request"
-	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 )
 
 var _ http.RoundTripper = &Context{}
 
 type Context struct {
-	body hcl.Body
+	body *hclsyntax.Body
 	rt   http.RoundTripper
 }
 
-func NewContext(body hcl.Body, rt http.RoundTripper) http.RoundTripper {
+func NewContext(body *hclsyntax.Body, rt http.RoundTripper) http.RoundTripper {
 	return &Context{
 		body: body,
 		rt:   rt,
diff --git a/config/ac_basic_auth.go b/config/ac_basic_auth.go
index ea3ed29a..cc9cb56f 100644
--- a/config/ac_basic_auth.go
+++ b/config/ac_basic_auth.go
@@ -3,6 +3,7 @@ package config
 import (
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config/body"
 	"github.com/avenga/couper/internal/seetie"
@@ -10,7 +11,10 @@ import (
 	"github.com/avenga/couper/config/meta"
 )
 
-var _ Inline = &BasicAuth{}
+var (
+	_ Body   = &BasicAuth{}
+	_ Inline = &BasicAuth{}
+)
 
 // BasicAuth represents the "basic_auth" config block
 type BasicAuth struct {
@@ -23,9 +27,9 @@ type BasicAuth struct {
 	Remain hcl.Body `hcl:",remain"`
 }
 
-// HCLBody implements the <Inline> interface. Internally used for 'error_handler'.
-func (b *BasicAuth) HCLBody() hcl.Body {
-	return b.Remain
+// HCLBody implements the <Body> interface. Internally used for 'error_handler'.
+func (b *BasicAuth) HCLBody() *hclsyntax.Body {
+	return b.Remain.(*hclsyntax.Body)
 }
 
 func (b *BasicAuth) Inline() interface{} {
@@ -54,9 +58,8 @@ func (b *BasicAuth) DefaultErrorHandler() *ErrorHandler {
 	}
 	return &ErrorHandler{
 		Kinds: []string{"basic_auth"},
-		Remain: body.New(&hcl.BodyContent{Attributes: map[string]*hcl.Attribute{
-			"set_response_headers": {Name: "set_response_headers", Expr: hcl.StaticExpr(seetie.MapToValue(map[string]interface{}{
-				"Www-Authenticate": wwwAuthenticateValue,
-			}), hcl.Range{Filename: "default_basic_auth_error_handler"})}}}),
+		Remain: body.NewHCLSyntaxBodyWithAttr("set_response_headers", seetie.MapToValue(map[string]interface{}{
+			"Www-Authenticate": wwwAuthenticateValue,
+		}), hcl.Range{Filename: "default_basic_auth_error_handler"}),
 	}
 }
diff --git a/config/ac_jwt.go b/config/ac_jwt.go
index a8290a78..d0a2e73c 100644
--- a/config/ac_jwt.go
+++ b/config/ac_jwt.go
@@ -5,13 +5,17 @@ import (
 
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config/meta"
 	"github.com/avenga/couper/errors"
 )
 
-var _ BackendInitialization = &JWT{}
-var _ Inline = &JWT{}
+var (
+	_ BackendInitialization = &JWT{}
+	_ Body                  = &JWT{}
+	_ Inline                = &JWT{}
+)
 
 // Claims represents the <Claims> object.
 type Claims hcl.Expression
@@ -43,7 +47,7 @@ type JWT struct {
 	TokenValue            hcl.Expression      `hcl:"token_value,optional" docs:"Expression to obtain the token. Cannot be used together with {cookie} or {header}." type:"string"`
 
 	// Internally used
-	Backend hcl.Body
+	Backend *hclsyntax.Body
 }
 
 func (j *JWT) Prepare(backendFunc PrepareBackendFunc) (err error) {
@@ -66,8 +70,8 @@ func (j *JWT) Reference() string {
 }
 
 // HCLBody implements the <Body> interface.
-func (j *JWT) HCLBody() hcl.Body {
-	return j.Remain
+func (j *JWT) HCLBody() *hclsyntax.Body {
+	return j.Remain.(*hclsyntax.Body)
 }
 
 // Inline implements the <Inline> interface.
@@ -89,11 +93,6 @@ func (j *JWT) Schema(inline bool) *hcl.BodySchema {
 
 	schema, _ := gohcl.ImpliedBodySchema(j.Inline())
 
-	// A backend reference is defined, backend block is not allowed.
-	if j.BackendName != "" {
-		schema.Blocks = nil
-	}
-
 	return meta.MergeSchemas(schema, meta.LogFieldsAttributeSchema)
 }
 
diff --git a/config/ac_oauth2.go b/config/ac_oauth2.go
index ae106ef4..92f6f9d3 100644
--- a/config/ac_oauth2.go
+++ b/config/ac_oauth2.go
@@ -5,6 +5,7 @@ import (
 
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config/meta"
 )
@@ -36,7 +37,7 @@ type OAuth2AC struct {
 	VerifierMethod          string   `hcl:"verifier_method" docs:"The method to verify the integrity of the authorization code flow. Available values: {ccm_s256} ({code_challenge} parameter with {code_challenge_method} {S256}), {state} ({state} parameter)"`
 
 	// internally used
-	Backend hcl.Body
+	Backend *hclsyntax.Body
 }
 
 func (oa *OAuth2AC) Prepare(backendFunc PrepareBackendFunc) (err error) {
@@ -49,9 +50,9 @@ func (oa *OAuth2AC) Reference() string {
 	return oa.BackendName
 }
 
-// HCLBody implements the <Inline> interface.
-func (oa *OAuth2AC) HCLBody() hcl.Body {
-	return oa.Remain
+// HCLBody implements the <Body> interface.
+func (oa *OAuth2AC) HCLBody() *hclsyntax.Body {
+	return oa.Remain.(*hclsyntax.Body)
 }
 
 // Inline implements the <Inline> interface.
@@ -74,11 +75,6 @@ func (oa *OAuth2AC) Schema(inline bool) *hcl.BodySchema {
 
 	schema, _ := gohcl.ImpliedBodySchema(oa.Inline())
 
-	// A backend reference is defined, backend block is not allowed.
-	if oa.BackendName != "" {
-		schema.Blocks = nil
-	}
-
 	return meta.MergeSchemas(schema, meta.LogFieldsAttributeSchema)
 }
 
diff --git a/config/ac_oidc.go b/config/ac_oidc.go
index 918f6fa6..18c0e55e 100644
--- a/config/ac_oidc.go
+++ b/config/ac_oidc.go
@@ -5,6 +5,7 @@ import (
 
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config/meta"
 )
@@ -12,6 +13,7 @@ import (
 var (
 	_ BackendReference      = &OIDC{}
 	_ BackendInitialization = &OIDC{}
+	_ Body                  = &OIDC{}
 	_ Inline                = &OIDC{}
 )
 
@@ -42,12 +44,12 @@ type OIDC struct {
 	UserinfoBackendName      string `hcl:"userinfo_backend,optional" docs:"Optional option to configure specific behavior for the backend to request the userinfo from."`
 
 	// internally used
-	Backends map[string]hcl.Body
+	Backends map[string]*hclsyntax.Body
 }
 
 func (o *OIDC) Prepare(backendFunc PrepareBackendFunc) (err error) {
 	if o.Backends == nil {
-		o.Backends = make(map[string]hcl.Body)
+		o.Backends = make(map[string]*hclsyntax.Body)
 	}
 
 	fields := BackendAttrFields(o)
@@ -66,9 +68,9 @@ func (o *OIDC) Reference() string {
 	return o.BackendName
 }
 
-// HCLBody implements the <Inline> interface.
-func (o *OIDC) HCLBody() hcl.Body {
-	return o.Remain
+// HCLBody implements the <Body> interface.
+func (o *OIDC) HCLBody() *hclsyntax.Body {
+	return o.Remain.(*hclsyntax.Body)
 }
 
 // Inline implements the <Inline> interface.
@@ -99,11 +101,6 @@ func (o *OIDC) Schema(inline bool) *hcl.BodySchema {
 
 	schema, _ := gohcl.ImpliedBodySchema(o.Inline())
 
-	// A backend reference is defined, backend block is not allowed.
-	if o.BackendName != "" {
-		schema.Blocks = nil
-	}
-
 	return meta.MergeSchemas(schema, meta.LogFieldsAttributeSchema)
 }
 
diff --git a/config/ac_saml.go b/config/ac_saml.go
index 199569a5..b79eedd3 100644
--- a/config/ac_saml.go
+++ b/config/ac_saml.go
@@ -3,11 +3,15 @@ package config
 import (
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config/meta"
 )
 
-var _ Inline = &SAML{}
+var (
+	_ Body   = &SAML{}
+	_ Inline = &SAML{}
+)
 
 // SAML represents the <SAML> object.
 type SAML struct {
@@ -24,8 +28,8 @@ type SAML struct {
 }
 
 // HCLBody implements the <Body> interface. Internally used for 'error_handler'.
-func (s *SAML) HCLBody() hcl.Body {
-	return s.Remain
+func (s *SAML) HCLBody() *hclsyntax.Body {
+	return s.Remain.(*hclsyntax.Body)
 }
 
 func (s *SAML) Inline() interface{} {
diff --git a/config/api.go b/config/api.go
index f1f7b9b2..2c78032b 100644
--- a/config/api.go
+++ b/config/api.go
@@ -3,11 +3,15 @@ package config
 import (
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config/meta"
 )
 
-var _ Inline = &API{}
+var (
+	_ Body   = &API{}
+	_ Inline = &API{}
+)
 
 // API represents the <API> object.
 type API struct {
@@ -30,9 +34,9 @@ type API struct {
 // APIs represents a list of <API> objects.
 type APIs []*API
 
-// HCLBody implements the <Inline> interface.
-func (a API) HCLBody() hcl.Body {
-	return a.Remain
+// HCLBody implements the <Body> interface.
+func (a API) HCLBody() *hclsyntax.Body {
+	return a.Remain.(*hclsyntax.Body)
 }
 
 // Inline implements the <Inline> interface.
diff --git a/config/backend.go b/config/backend.go
index ad26fbca..1a71bc95 100644
--- a/config/backend.go
+++ b/config/backend.go
@@ -3,12 +3,14 @@ package config
 import (
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config/meta"
 )
 
 var (
 	_ BackendReference = &Backend{}
+	_ Body             = &Backend{}
 	_ Inline           = &Backend{}
 
 	BackendInlineSchema = Backend{}.Schema(true)
@@ -32,9 +34,9 @@ func (b Backend) Reference() string {
 	return b.Name
 }
 
-// HCLBody implements the <Inline> interface.
-func (b Backend) HCLBody() hcl.Body {
-	return b.Remain
+// HCLBody implements the <Body> interface.
+func (b Backend) HCLBody() *hclsyntax.Body {
+	return b.Remain.(*hclsyntax.Body)
 }
 
 // Inline implements the <Inline> interface.
diff --git a/config/body/body.go b/config/body/body.go
index 679c1d7d..f54d3fcc 100644
--- a/config/body/body.go
+++ b/config/body/body.go
@@ -6,59 +6,50 @@ import (
 	"github.com/zclconf/go-cty/cty"
 )
 
-var _ hcl.Body = &Body{}
-
-type Attributes interface {
-	JustAllAttributes() []hcl.Attributes
-	JustAllAttributesWithName(string) []hcl.Attributes
-}
-
-type Body struct {
-	content *hcl.BodyContent
-}
-
-func New(content *hcl.BodyContent) hcl.Body {
-	return &Body{content}
-}
-
-func NewContentWithAttrName(name, value string) *hcl.BodyContent {
-	return NewContentWithAttr(&hcl.Attribute{
-		Name: name,
-		Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal(value)},
-	})
-}
-
-func NewContentWithAttr(attr *hcl.Attribute) *hcl.BodyContent {
-	return &hcl.BodyContent{Attributes: map[string]*hcl.Attribute{
-		attr.Name: attr,
-	}}
-}
-
-func RenameAttribute(content *hcl.BodyContent, old, new string) {
-	if attr, ok := content.Attributes[old]; ok {
-		attr.Name = new
-		content.Attributes[new] = attr
-		delete(content.Attributes, old)
+func NewHCLSyntaxBodyWithAttr(name string, value cty.Value, rng hcl.Range) *hclsyntax.Body {
+	return &hclsyntax.Body{
+		Attributes: hclsyntax.Attributes{
+			name: {
+				Name: name,
+				Expr: &hclsyntax.LiteralValueExpr{Val: value, SrcRange: rng},
+			},
+		},
 	}
 }
 
-func (e *Body) Content(_ *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
-	return e.content, nil
+func NewHCLSyntaxBodyWithStringAttr(name, value string) *hclsyntax.Body {
+	return NewHCLSyntaxBodyWithAttr(name, cty.StringVal(value), hcl.Range{})
 }
 
-func (e *Body) PartialContent(_ *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
-	return e.content, e, nil
+func MergeBodies(dest, src *hclsyntax.Body, replace bool) *hclsyntax.Body {
+	if src == dest {
+		return dest
+	}
+	for k, v := range src.Attributes {
+		if _, set := dest.Attributes[k]; replace || !set {
+			dest.Attributes[k] = v
+		}
+	}
+	for _, bl := range src.Blocks {
+		dest.Blocks = append(dest.Blocks, bl)
+	}
+	return dest
 }
 
-func (e *Body) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
-	attrs := hcl.Attributes{}
-	for k, v := range e.content.Attributes {
-		cv := *v
-		attrs[k] = &cv
+func BlocksOfType(body *hclsyntax.Body, blockType string) []*hclsyntax.Block {
+	var blocks []*hclsyntax.Block
+	for _, bl := range body.Blocks {
+		if bl.Type == blockType {
+			blocks = append(blocks, bl)
+		}
 	}
-	return attrs, nil
+	return blocks
 }
 
-func (e *Body) MissingItemRange() hcl.Range {
-	return e.content.MissingItemRange
+func RenameAttribute(body *hclsyntax.Body, old, new string) {
+	if attr, ok := body.Attributes[old]; ok {
+		attr.Name = new
+		body.Attributes[new] = attr
+		delete(body.Attributes, old)
+	}
 }
diff --git a/config/body/collect.go b/config/body/collect.go
index 0ecd2169..120ddc71 100644
--- a/config/body/collect.go
+++ b/config/body/collect.go
@@ -5,44 +5,17 @@ import (
 	"github.com/hashicorp/hcl/v2/hclsyntax"
 )
 
-func CollectAttributes(bodies ...hcl.Body) []*hcl.Attribute {
-	allAttributes := make([]*hcl.Attribute, 0)
+func CollectAttributes(bodies ...hcl.Body) []*hclsyntax.Attribute {
+	allAttributes := make([]*hclsyntax.Attribute, 0)
 
 	for _, b := range bodies {
-		switch sb := b.(type) {
-		case *hclsyntax.Body:
-			for _, attr := range sb.Attributes {
-				allAttributes = append(allAttributes, &hcl.Attribute{
-					Name:      attr.Name,
-					Expr:      attr.Expr,
-					Range:     attr.SrcRange,
-					NameRange: attr.NameRange,
-				})
-			}
-
-			for _, block := range sb.Blocks {
-				allAttributes = append(allAttributes, CollectAttributes(block.Body)...)
-			}
-		case *Body:
-			content, _, _ := sb.PartialContent(nil)
-			for _, attr := range content.Attributes {
-				allAttributes = append(allAttributes, attr)
-			}
-			for _, block := range content.Blocks {
-				allAttributes = append(allAttributes, CollectAttributes(block.Body)...)
-			}
-		case MergedBodies:
-			// top-level attrs
-			for _, attrs := range sb.JustAllAttributes() {
-				for _, attr := range attrs {
-					allAttributes = append(allAttributes, attr)
-				}
-			}
+		sb, _ := b.(*hclsyntax.Body)
+		for _, attr := range sb.Attributes {
+			allAttributes = append(allAttributes, attr)
+		}
 
-			// nested block attrs
-			for _, mb := range sb {
-				allAttributes = append(allAttributes, CollectAttributes(mb)...)
-			}
+		for _, block := range sb.Blocks {
+			allAttributes = append(allAttributes, CollectAttributes(block.Body)...)
 		}
 	}
 
@@ -62,25 +35,10 @@ func CollectBlockTypes(bodies ...hcl.Body) []string {
 	}
 
 	for _, b := range bodies {
-		switch sb := b.(type) {
-		case *hclsyntax.Body:
-
-			for _, block := range sb.Blocks {
-				nested := append(append([]string{}, block.Type), CollectBlockTypes(block.Body)...)
-				addUniqueFn(nested...)
-			}
-		case *Body:
-			content, _, _ := sb.PartialContent(nil)
-			for _, block := range content.Blocks {
-				nested := append(append([]string{}, block.Type), CollectBlockTypes(block.Body)...)
-				addUniqueFn(nested...)
-			}
-		case MergedBodies:
-			// nested block
-			for _, mb := range sb {
-				nested := append([]string{}, CollectBlockTypes(mb)...)
-				addUniqueFn(nested...)
-			}
+		sb, _ := b.(*hclsyntax.Body)
+		for _, block := range sb.Blocks {
+			nested := append(append([]string{}, block.Type), CollectBlockTypes(block.Body)...)
+			addUniqueFn(nested...)
 		}
 	}
 
@@ -92,8 +50,8 @@ func CollectBlockTypes(bodies ...hcl.Body) []string {
 	return result
 }
 
-func CollectExpressions(bodies ...hcl.Body) []hcl.Expression {
-	allExpressions := make([]hcl.Expression, 0)
+func CollectExpressions(bodies ...hcl.Body) []hclsyntax.Expression {
+	allExpressions := make([]hclsyntax.Expression, 0)
 	for _, attr := range CollectAttributes(bodies...) {
 		allExpressions = append(allExpressions, attr.Expr)
 	}
diff --git a/config/body/merged.go b/config/body/merged.go
deleted file mode 100644
index fb7614d5..00000000
--- a/config/body/merged.go
+++ /dev/null
@@ -1,307 +0,0 @@
-// File based on "github.com/hashicorp/hcl/v2/merged.go" except diagnostic errors for duplicates
-// since we explicitly want them and apply an override.
-
-package body
-
-import (
-	"fmt"
-	"sort"
-	"strings"
-
-	"github.com/hashicorp/hcl/v2"
-	"github.com/hashicorp/hcl/v2/hclsyntax"
-)
-
-// reportDuplicates is the global switch to handle force merges.
-var reportDuplicates = false
-
-// MergeBodies is like MergeFiles except it deals directly with bodies, rather
-// than with entire files.
-func MergeBodies(bodies ...hcl.Body) hcl.Body {
-	if len(bodies) == 0 {
-		// Swap out for our singleton empty body, to reduce the number of
-		// empty slices we have hanging around.
-		return EmptyBody()
-	}
-
-	// If any of the given bodies are already merged bodies, we'll unpack
-	// to flatten to a single MergedBodies, since that's conceptually simpler.
-	// This also, as a side-effect, eliminates any empty bodies, since
-	// empties are merged bodies with no inner bodies.
-	var newLen int
-	var flatten bool
-	for _, body := range bodies {
-		if children, merged := body.(MergedBodies); merged {
-			newLen += len(children)
-			flatten = true
-		} else {
-			newLen++
-		}
-	}
-
-	if !flatten { // not just newLen == len, because we might have MergedBodies with single bodies inside
-		return MergedBodies(bodies)
-	}
-
-	if newLen == 0 {
-		// Don't allocate a new empty when we already have one
-		return emptyBody
-	}
-
-	newBodies := make([]hcl.Body, 0, newLen)
-	for _, body := range bodies {
-		if children, merged := body.(MergedBodies); merged {
-			newBodies = append(newBodies, children...)
-		} else {
-			newBodies = append(newBodies, body)
-		}
-	}
-	return MergedBodies(newBodies)
-}
-
-var emptyBody = MergedBodies([]hcl.Body{})
-
-// EmptyBody returns a body with no content. This body can be used as a
-// placeholder when a body is required but no body content is available.
-func EmptyBody() hcl.Body {
-	return emptyBody
-}
-
-type MergedBodies []hcl.Body
-
-// Content returns the content produced by applying the given schema to all
-// of the merged bodies and merging the result.
-//
-// Although required attributes _are_ supported, they should be used sparingly
-// with merged bodies since in this case there is no contextual information
-// with which to return good diagnostics. Applications working with merged
-// bodies may wish to mark all attributes as optional and then check for
-// required attributes afterwards, to produce better diagnostics.
-func (mb MergedBodies) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
-	// the returned body will always be empty in this case, because mergedContent
-	// will only ever call Content on the child bodies.
-	content, _, diags := mb.mergedContent(schema, false)
-	return content, diags
-}
-
-func (mb MergedBodies) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
-	return mb.mergedContent(schema, true)
-}
-
-func (mb MergedBodies) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
-	attrs := make(map[string]*hcl.Attribute)
-	var diags hcl.Diagnostics
-
-	for _, body := range mb {
-		thisAttrs, thisDiags := body.JustAttributes()
-
-		if len(thisDiags) > 0 {
-			diags = append(diags, thisDiags...)
-		}
-
-		if thisAttrs != nil {
-			diags = append(diags, mergeAttributes(attrs, thisAttrs)...)
-		}
-	}
-
-	return attrs, diags
-}
-
-// MissingItemRange returns the first non-empty range if any.
-func (mb MergedBodies) MissingItemRange() hcl.Range {
-	if len(mb) == 0 {
-		// Nothing useful to return here, so we'll return some garbage.
-		return hcl.Range{
-			Filename: "<empty>",
-		}
-	}
-
-	for _, b := range mb {
-		if be, ok := b.(*hclsyntax.Body); ok {
-			if !be.SrcRange.Empty() {
-				return be.SrcRange
-			}
-		} else {
-			if !b.MissingItemRange().Empty() {
-				return b.MissingItemRange()
-			}
-		}
-	}
-
-	// arbitrarily use the first body's missing item range
-	return mb[0].MissingItemRange()
-}
-
-func (mb MergedBodies) mergedContent(schema *hcl.BodySchema, partial bool) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
-	// We need to produce a new schema with none of the attributes marked as
-	// required, since _any one_ of our bodies can contribute an attribute value.
-	// We'll separately check that all required attributes are present at
-	// the end.
-	mergedSchema := &hcl.BodySchema{
-		Blocks: schema.Blocks,
-	}
-	for _, attrS := range schema.Attributes {
-		mergedAttrS := attrS
-		mergedAttrS.Required = false
-		mergedSchema.Attributes = append(mergedSchema.Attributes, mergedAttrS)
-	}
-
-	var mergedLeftovers []hcl.Body
-	content := &hcl.BodyContent{
-		Attributes: map[string]*hcl.Attribute{},
-	}
-
-	var diags hcl.Diagnostics
-	for _, body := range mb {
-		var thisContent *hcl.BodyContent
-		var thisLeftovers hcl.Body
-		var thisDiags hcl.Diagnostics
-
-		if partial {
-			thisContent, thisLeftovers, thisDiags = body.PartialContent(mergedSchema)
-		} else {
-			thisContent, thisDiags = body.Content(mergedSchema)
-		}
-
-		if thisLeftovers != nil {
-			mergedLeftovers = append(mergedLeftovers, thisLeftovers)
-		}
-		if len(thisDiags) > 0 {
-			diags = append(diags, thisDiags...)
-		}
-
-		if thisContent.Attributes != nil {
-			diags = append(diags, mergeAttributes(content.Attributes, thisContent.Attributes)...)
-		}
-
-		if len(thisContent.Blocks) != 0 {
-			for _, thisContentBlock := range thisContent.Blocks {
-				// Merge blocks keyed by type and labels.
-				var contentBlockType string
-				var idx = 0
-				for i, contentBlock := range content.Blocks {
-					if contentBlock.Type == thisContentBlock.Type {
-						if !StringSliceEquals(contentBlock.Labels, thisContentBlock.Labels) {
-							continue
-						}
-
-						contentBlockType = contentBlock.Type
-						idx = i
-						break
-					}
-				}
-
-				if contentBlockType == "" { // nothing found
-					content.Blocks = append(content.Blocks, thisContentBlock)
-					continue
-				}
-
-				contentAttrs, contentAttrsDiags := content.Blocks[idx].Body.JustAttributes()
-				if contentAttrsDiags.HasErrors() {
-					diags = append(diags, contentAttrsDiags...)
-				}
-				thisContentAttrs, thisContentAttrsDiags := thisContentBlock.Body.JustAttributes()
-				if thisContentAttrsDiags.HasErrors() {
-					diags = append(diags, thisContentAttrsDiags...)
-				}
-
-				diags = append(diags, mergeAttributes(contentAttrs, thisContentAttrs)...)
-
-				content.Blocks[idx].Body = MergeBodies(
-					thisContentBlock.Body, // keep child blocks
-					New(&hcl.BodyContent{
-						Attributes:       contentAttrs,
-						MissingItemRange: content.Blocks[idx].DefRange}),
-				)
-			}
-		}
-	}
-
-	// Finally, we check for required attributes.
-	for _, attrS := range schema.Attributes {
-		if !attrS.Required {
-			continue
-		}
-
-		itemRange := content.MissingItemRange
-		if itemRange.Empty() { // use parent range as fallback
-			itemRange = mb.MissingItemRange()
-		}
-
-		if content.Attributes[attrS.Name] == nil {
-			// We don't have any context here to produce a good diagnostic,
-			// which is why we warn in the Content docstring to minimize the
-			// use of required attributes on merged bodies.
-			diags = diags.Append(&hcl.Diagnostic{
-				Severity: hcl.DiagError,
-				Subject:  &itemRange,
-				Summary:  "Missing required argument",
-				Detail: fmt.Sprintf(
-					"The argument %q is required, but was not set.",
-					attrS.Name,
-				),
-			})
-		}
-	}
-
-	leftoverBody := MergeBodies(mergedLeftovers...)
-	return content, leftoverBody, diags
-}
-
-// JustAllAttributes returns a list of attributes in order. Since these bodies got added as partialContent
-// we do not have to supply a scheme or handle the underlying diagnostics.
-func (mb MergedBodies) JustAllAttributes() []hcl.Attributes {
-	return mb.JustAllAttributesWithName("")
-}
-
-// JustAllAttributesWithName behaviour is the same as JustAllAttributes with a filtered slice.
-func (mb MergedBodies) JustAllAttributesWithName(name string) []hcl.Attributes {
-	var result []hcl.Attributes
-
-	for _, body := range mb {
-		attrs, _ := body.JustAttributes()
-		for attrName := range attrs {
-			if name != "" && attrName != name {
-				delete(attrs, attrName)
-			}
-		}
-		if len(attrs) > 0 {
-			result = append(result, attrs)
-		}
-	}
-	return result
-}
-
-func mergeAttributes(left, right hcl.Attributes) (diags hcl.Diagnostics) {
-	for name, attr := range right {
-		if existing := left[name]; reportDuplicates && existing != nil {
-			diags = diags.Append(&hcl.Diagnostic{
-				Severity: hcl.DiagError,
-				Summary:  "Duplicate argument",
-				Detail: fmt.Sprintf(
-					"Argument %q was already set at %s",
-					name, existing.NameRange.String(),
-				),
-				Subject: &attr.NameRange,
-			})
-			continue
-		}
-		left[name] = attr
-	}
-	return diags
-}
-
-func StringSliceEquals(left, right []string) bool {
-	const sep = "🍫"
-	if left == nil {
-		return right == nil
-	} else if right == nil {
-		return left == nil
-	}
-
-	thatLabels := left[:]
-	sort.Strings(thatLabels)
-	thisLabels := right[:]
-	sort.Strings(thisLabels)
-	return strings.Join(thatLabels, sep) == strings.Join(thisLabels, sep)
-}
diff --git a/config/configload/backend.go b/config/configload/backend.go
index 441bf17e..1939aebc 100644
--- a/config/configload/backend.go
+++ b/config/configload/backend.go
@@ -22,30 +22,32 @@ var backendBlockSchema = &hcl.BodySchema{
 	},
 }
 
-var defaultBackend = hclbody.New(&hcl.BodyContent{
-	Attributes: map[string]*hcl.Attribute{
-		"connect_timeout": {
-			Name: "connect_timeout",
-			Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("10s")},
-		},
-		"ttfb_timeout": {
-			Name: "ttfb_timeout",
-			Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("60s")},
-		},
-		"timeout": {
-			Name: "timeout",
-			Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("300s")},
+func newDefaultBackend() *hclsyntax.Body {
+	return &hclsyntax.Body{
+		Attributes: map[string]*hclsyntax.Attribute{
+			"connect_timeout": {
+				Name: "connect_timeout",
+				Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("10s")},
+			},
+			"ttfb_timeout": {
+				Name: "ttfb_timeout",
+				Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("60s")},
+			},
+			"timeout": {
+				Name: "timeout",
+				Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("300s")},
+			},
 		},
-	},
-})
+	}
+}
 
 // PrepareBackend is a method which is mandatory to call for preparing any kind of backend.
 // This applies to defined, reference, anonymous and endpoint/url related configurations.
 // This method will be called recursively and is used as wrapped injector for
 // access-control backends via config.PrepareBackendFunc.
-func PrepareBackend(helper *helper, attrName, attrValue string, block config.Inline) (hcl.Body, error) {
+func PrepareBackend(helper *helper, attrName, attrValue string, block config.Body) (*hclsyntax.Body, error) {
 	var reference string // backend definitions
-	var backendBody hcl.Body
+	var backendBody *hclsyntax.Body
 	var err error
 
 	reference, backendBody, err = getBackendReference(block)
@@ -69,7 +71,7 @@ func PrepareBackend(helper *helper, attrName, attrValue string, block config.Inl
 
 		if backendBody == nil {
 			if attrName == "_init" { // initial definitions case
-				backendBody = hclbody.MergeBodies(defaultBackend, refBody)
+				backendBody = hclbody.MergeBodies(refBody, newDefaultBackend(), false)
 			} else { // plain reference without params
 				return refBody, nil
 			}
@@ -80,9 +82,7 @@ func PrepareBackend(helper *helper, attrName, attrValue string, block config.Inl
 			if err = invalidOriginRefinement(refBody, backendBody); err != nil {
 				return nil, err
 			}
-			backendBody = hclbody.MergeBodies(
-				hclbody.New(hclbody.NewContentWithAttrName("name", reference)),
-				backendBody)
+			setName(reference, backendBody)
 			// no child blocks are allowed, so no need to try to wrap with oauth2 or token request
 			return backendBody, nil
 		}
@@ -97,36 +97,43 @@ func PrepareBackend(helper *helper, attrName, attrValue string, block config.Inl
 		anonLabel := newAnonLabel(block.HCLBody(), labelRange) + labelSuffix
 		// ensure our default settings
 		if backendBody == nil {
-			backendBody = defaultBackend
+			backendBody = newDefaultBackend()
 		} else {
-			backendBody = hclbody.MergeBodies(defaultBackend, backendBody)
+			switch labelSuffix {
+			case "_jwks_uri_backend", "_token_backend", "_userinfo_backend":
+				copied := *backendBody
+				// create new Attributes to allow different name later
+				copied.Attributes = make(map[string]*hclsyntax.Attribute, len(backendBody.Attributes))
+				for k, v := range backendBody.Attributes {
+					copied.Attributes[k] = v
+				}
+				backendBody = hclbody.MergeBodies(&copied, newDefaultBackend(), false)
+			default:
+				// with OIDC this is used for _configuration_backend
+				backendBody = hclbody.MergeBodies(backendBody, newDefaultBackend(), false)
+			}
 		}
 
-		backendBody = newBodyWithName(anonLabel, backendBody)
+		setName(anonLabel, backendBody)
 	}
 
 	// watch out for oauth blocks and nested backend definitions
-	backendBody, err = wrapOAuthBackend(helper, backendBody)
+	backendBody, err = setOAuth2Backend(helper, backendBody)
 	if err != nil {
 		return nil, err
 	}
 
 	// watch out for beta_token_request blocks and nested backend definitions
-	return wrapTokenRequestBackend(helper, backendBody)
+	return setTokenRequestBackend(helper, backendBody)
 }
 
 // getBackendReference reads a referenced backend name and the refined backend block content if any.
-func getBackendReference(inline config.Inline) (string, hcl.Body, error) {
+func getBackendReference(b config.Body) (string, *hclsyntax.Body, error) {
 	var reference string
 
-	content, _, diags := inline.HCLBody().PartialContent(inline.Schema(true))
-	if diags.HasErrors() {
-		return "", nil, diags
-	}
-
-	backends := content.Blocks.OfType(backend)
+	backends := hclbody.BlocksOfType(b.HCLBody(), backend)
 	if len(backends) == 0 {
-		if beref, ok := inline.(config.BackendReference); ok {
+		if beref, ok := b.(config.BackendReference); ok {
 			if beref.Reference() != "" {
 				reference = beref.Reference()
 			}
@@ -141,15 +148,10 @@ func getBackendReference(inline config.Inline) (string, hcl.Body, error) {
 	return reference, body, nil
 }
 
-// wrapOAuthBackend prepares a nested backend within a backend-oauth2 block.
+// setOAuth2Backend prepares a nested backend within a backend-oauth2 block.
 // TODO: Check a possible circular dependency with given parent backend(s).
-func wrapOAuthBackend(helper *helper, parent hcl.Body) (hcl.Body, error) {
-	innerContent, _, diags := parent.PartialContent(config.OAuthBlockSchema)
-	if diags.HasErrors() {
-		return nil, diags
-	}
-
-	oauthBlocks := innerContent.Blocks.OfType(oauth2)
+func setOAuth2Backend(helper *helper, parent *hclsyntax.Body) (*hclsyntax.Body, error) {
+	oauthBlocks := hclbody.BlocksOfType(parent, oauth2)
 	if len(oauthBlocks) == 0 {
 		return parent, nil
 	}
@@ -157,7 +159,7 @@ func wrapOAuthBackend(helper *helper, parent hcl.Body) (hcl.Body, error) {
 	// oauth block exists, read out backend configuration
 	oauthBody := oauthBlocks[0].Body
 	conf := &config.OAuth2ReqAuth{}
-	if diags = gohcl.DecodeBody(oauthBody, helper.context, conf); diags.HasErrors() {
+	if diags := gohcl.DecodeBody(oauthBody, helper.context, conf); diags.HasErrors() {
 		return nil, diags
 	}
 
@@ -166,28 +168,29 @@ func wrapOAuthBackend(helper *helper, parent hcl.Body) (hcl.Body, error) {
 		return nil, err
 	}
 
-	wrapped := wrapBlock(oauth2, oauthBlocks[0].Labels, backendBody)
-	parent = hclbody.MergeBodies(parent, wrapped)
+	backendBlock := &hclsyntax.Block{
+		Type: backend,
+		Body: backendBody,
+	}
+	oauthBody.Blocks = append(oauthBody.Blocks, backendBlock)
 
 	return parent, nil
 }
 
-func checkTokenRequestLabels(backendBody hcl.Body, unique map[string]struct{}) error {
-	ic, _, diags := backendBody.PartialContent(config.TokenRequestBlockSchema)
-	if diags.HasErrors() {
-		return diags
-	}
-
-	trbs := ic.Blocks.OfType(tokenRequest)
+func checkTokenRequestLabels(trbs []*hclsyntax.Block, unique map[string]struct{}) error {
 	for _, trb := range trbs {
 		label := defaultNameLabel
-		r := &trb.DefRange
+		dr := trb.DefRange()
+		r := &dr
 		if len(trb.Labels) > 0 {
 			label = trb.Labels[0]
 			r = &trb.LabelRanges[0]
 			if err := validLabel(label, r); err != nil {
 				return err
 			}
+		} else {
+			// add "default" label if no label is configured
+			trb.Labels = append(trb.Labels, label)
 		}
 
 		if err := uniqueLabelName("token request", unique, label, r); err != nil {
@@ -198,99 +201,58 @@ func checkTokenRequestLabels(backendBody hcl.Body, unique map[string]struct{}) e
 	return nil
 }
 
-// wrapTokenRequestBackend prepares a nested backend within each backend-tokenRequest block.
+// setTokenRequestBackend prepares a nested backend within each backend-tokenRequest block.
 // TODO: Check a possible circular dependency with given parent backend(s).
-func wrapTokenRequestBackend(helper *helper, parent hcl.Body) (hcl.Body, error) {
-	unique := map[string]struct{}{}
-	if mb, ok := parent.(hclbody.MergedBodies); ok {
-		for _, bo := range mb {
-			err := checkTokenRequestLabels(bo, unique)
-			if err != nil {
-				return nil, err
-			}
-		}
-	} else {
-		err := checkTokenRequestLabels(parent, unique)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	innerContent, _, diags := parent.PartialContent(config.TokenRequestBlockSchema)
-	if diags.HasErrors() {
-		return nil, diags
-	}
-
-	tokenRequestBlocks := innerContent.Blocks.OfType(tokenRequest)
+func setTokenRequestBackend(helper *helper, parent *hclsyntax.Body) (*hclsyntax.Body, error) {
+	tokenRequestBlocks := hclbody.BlocksOfType(parent, tokenRequest)
 	if len(tokenRequestBlocks) == 0 {
 		return parent, nil
 	}
 
+	unique := map[string]struct{}{}
+	err := checkTokenRequestLabels(tokenRequestBlocks, unique)
+	if err != nil {
+		return nil, err
+	}
+
 	// beta_token_request block exists, read out backend configuration
 	for _, tokenRequestBlock := range tokenRequestBlocks {
 		tokenRequestBody := tokenRequestBlock.Body
 		conf := &config.TokenRequest{}
-		if diags = gohcl.DecodeBody(tokenRequestBody, helper.context, conf); diags.HasErrors() {
-			return nil, diags
-		}
-
-		content, leftOvers, diags := conf.Remain.PartialContent(conf.Schema(true))
-		if diags.HasErrors() {
+		if diags := gohcl.DecodeBody(tokenRequestBody, helper.context, conf); diags.HasErrors() {
 			return nil, diags
 		}
 
-		if err := verifyBodyAttributes(tokenRequest, content); err != nil {
+		if err := verifyBodyAttributes(tokenRequest, tokenRequestBody); err != nil {
 			return nil, err
 		}
 
-		hclbody.RenameAttribute(content, "headers", "set_request_headers")
-		hclbody.RenameAttribute(content, "query_params", "set_query_params")
-		conf.Remain = hclbody.MergeBodies(leftOvers, hclbody.New(content))
+		hclbody.RenameAttribute(tokenRequestBody, "headers", "set_request_headers")
+		hclbody.RenameAttribute(tokenRequestBody, "query_params", "set_query_params")
 
-		tokenRequestBackend, berr := PrepareBackend(helper, "", conf.URL, conf)
+		backendBody, berr := PrepareBackend(helper, "", conf.URL, conf)
 		if berr != nil {
 			return nil, berr
 		}
 
-		wrapped := wrapBlock(tokenRequest, tokenRequestBlock.Labels,
-			hclbody.MergeBodies(conf.Remain, tokenRequestBackend))
-		parent = hclbody.MergeBodies(parent, wrapped)
+		backendBlock := &hclsyntax.Block{
+			Type: backend,
+			Body: hclbody.MergeBodies(backendBody, tokenRequestBody, false),
+		}
+		tokenRequestBody.Blocks = append(tokenRequestBody.Blocks, backendBlock)
 	}
 
 	return parent, nil
 }
 
-func wrapBlock(blockType string, labels []string, content hcl.Body) hcl.Body {
-	return hclbody.New(&hcl.BodyContent{
-		Blocks: []*hcl.Block{
-			{
-				Body:   newBlock(backend, content),
-				Labels: labels,
-				Type:   blockType,
-			},
-		},
-	})
-}
-
-func newBlock(blockType string, content hcl.Body) hcl.Body {
-	return hclbody.New(&hcl.BodyContent{
-		Blocks: []*hcl.Block{
-			{
-				Body: content,
-				Type: blockType,
-			},
-		},
-	})
-}
-
-func newBodyWithName(nameValue string, config hcl.Body) hcl.Body {
-	return hclbody.MergeBodies(
-		config,
-		hclbody.New(hclbody.NewContentWithAttrName("name", nameValue)),
-	)
+func setName(nameValue string, backendBody *hclsyntax.Body) {
+	backendBody.Attributes["name"] = &hclsyntax.Attribute{
+		Name: "name",
+		Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal(nameValue)},
+	}
 }
 
-func newAnonLabel(body hcl.Body, labelRange *hcl.Range) string {
+func newAnonLabel(body *hclsyntax.Body, labelRange *hcl.Range) string {
 	const anon = "anonymous"
 	itemRange := labelRange
 	if itemRange == nil {
@@ -303,12 +265,10 @@ func newAnonLabel(body hcl.Body, labelRange *hcl.Range) string {
 	)
 }
 
-func refineAnonLabel(attrName string, body hcl.Body) (labelRange *hcl.Range, labelSuffix string) {
-	if syntaxBody, ok := body.(*hclsyntax.Body); ok {
-		if attr, exist := syntaxBody.Attributes[attrName]; exist {
-			labelRange = &attr.NameRange
-		}
-		labelSuffix += "_" + attrName
+func refineAnonLabel(attrName string, body *hclsyntax.Body) (labelRange *hcl.Range, labelSuffix string) {
+	if attr, exist := body.Attributes[attrName]; exist {
+		labelRange = &attr.NameRange
 	}
+	labelSuffix += "_" + attrName
 	return labelRange, labelSuffix
 }
diff --git a/config/configload/endpoint.go b/config/configload/endpoint.go
index c84ae62b..e57c2ec7 100644
--- a/config/configload/endpoint.go
+++ b/config/configload/endpoint.go
@@ -1,7 +1,6 @@
 package configload
 
 import (
-	"fmt"
 	"net/http"
 
 	"github.com/hashicorp/hcl/v2"
@@ -15,20 +14,11 @@ import (
 )
 
 func newCatchAllEndpoint() *config.Endpoint {
-	responseBody := hclbody.New(&hcl.BodyContent{
-		Attributes: map[string]*hcl.Attribute{
-			"status": {
-				Name: "status",
-				Expr: &hclsyntax.LiteralValueExpr{
-					Val: cty.NumberIntVal(http.StatusNotFound),
-				},
-			},
-		},
-	})
+	responseBody := hclbody.NewHCLSyntaxBodyWithAttr("status", cty.NumberIntVal(http.StatusNotFound), hcl.Range{})
 
 	return &config.Endpoint{
 		Pattern: "/**",
-		Remain:  hclbody.New(&hcl.BodyContent{}),
+		Remain:  &hclsyntax.Body{},
 		Response: &config.Response{
 			Remain: responseBody,
 		},
@@ -42,33 +32,33 @@ func refineEndpoints(helper *helper, endpoints config.Endpoints, check bool) err
 		if check && endpoint.Pattern == "" {
 			var r *hcl.Range
 			if endpoint.Remain != nil {
-				r = getRange(endpoint.Remain)
+				r = getRange(endpoint.HCLBody())
 			}
 			return newDiagErr(r, "endpoint: missing path pattern")
 		}
 
-		endpointContent := bodyToContent(endpoint.Remain)
-
-		rp := endpointContent.Attributes["beta_required_permission"]
+		endpointBody := endpoint.HCLBody()
+		rp := endpointBody.Attributes["beta_required_permission"]
 		if rp != nil {
 			endpoint.RequiredPermission = rp.Expr
 		}
 
 		if check && endpoint.AllowedMethods != nil && len(endpoint.AllowedMethods) > 0 {
-			if err = validMethods(endpoint.AllowedMethods, &endpointContent.Attributes["allowed_methods"].Range); err != nil {
+			if err = validMethods(endpoint.AllowedMethods, endpointBody.Attributes["allowed_methods"]); err != nil {
 				return err
 			}
 		}
 
 		if check && len(endpoint.Proxies)+len(endpoint.Requests) == 0 && endpoint.Response == nil {
-			return newDiagErr(&endpointContent.MissingItemRange,
+			r := endpointBody.MissingItemRange()
+			return newDiagErr(&r,
 				"missing 'default' proxy or request block, or a response definition",
 			)
 		}
 
 		proxyRequestLabelRequired := len(endpoint.Proxies)+len(endpoint.Requests) > 1
 
-		names := map[string]hcl.Body{}
+		names := map[string]*hclsyntax.Body{}
 		unique := map[string]struct{}{}
 		subject := endpoint.Remain.MissingItemRange()
 
@@ -77,14 +67,13 @@ func refineEndpoints(helper *helper, endpoints config.Endpoints, check bool) err
 				proxyConfig.Name = defaultNameLabel
 			}
 
-			names[proxyConfig.Name] = proxyConfig.Remain
+			names[proxyConfig.Name] = proxyConfig.HCLBody()
 
 			if err = validLabel(proxyConfig.Name, &subject); err != nil {
 				return err
 			}
 
 			if proxyRequestLabelRequired {
-				// TODO add test
 				if err = uniqueLabelName("proxy and request", unique, proxyConfig.Name, &subject); err != nil {
 					return err
 				}
@@ -104,7 +93,7 @@ func refineEndpoints(helper *helper, endpoints config.Endpoints, check bool) err
 				}
 
 				if wsBody != nil {
-					proxyConfig.Remain = hclbody.MergeBodies(proxyConfig.Remain, wsBody)
+					proxyConfig.Remain = hclbody.MergeBodies(proxyConfig.HCLBody(), wsBody, true)
 				}
 			}
 
@@ -119,33 +108,26 @@ func refineEndpoints(helper *helper, endpoints config.Endpoints, check bool) err
 				reqConfig.Name = defaultNameLabel
 			}
 
-			names[reqConfig.Name] = reqConfig.Remain
+			names[reqConfig.Name] = reqConfig.HCLBody()
 
 			if err = validLabel(reqConfig.Name, &subject); err != nil {
 				return err
 			}
 
 			if proxyRequestLabelRequired {
-				// TODO add test
 				if err = uniqueLabelName("proxy and request", unique, reqConfig.Name, &subject); err != nil {
 					return err
 				}
 			}
 
 			// remap request specific names for headers and query to well known ones
-			content, leftOvers, diags := reqConfig.Remain.PartialContent(reqConfig.Schema(true))
-			if diags.HasErrors() {
-				return diags
-			}
-
-			if err = verifyBodyAttributes(request, content); err != nil {
+			reqBody := reqConfig.HCLBody()
+			if err = verifyBodyAttributes(request, reqBody); err != nil {
 				return err
 			}
 
-			hclbody.RenameAttribute(content, "headers", "set_request_headers")
-			hclbody.RenameAttribute(content, "query_params", "set_query_params")
-
-			reqConfig.Remain = hclbody.MergeBodies(leftOvers, hclbody.New(content))
+			hclbody.RenameAttribute(reqBody, "headers", "set_request_headers")
+			hclbody.RenameAttribute(reqBody, "query_params", "set_query_params")
 
 			reqConfig.Backend, err = PrepareBackend(helper, "", "", reqConfig)
 			if err != nil {
@@ -176,32 +158,27 @@ func refineEndpoints(helper *helper, endpoints config.Endpoints, check bool) err
 	return nil
 }
 
-func getWebsocketsConfig(proxyConfig *config.Proxy) (bool, hcl.Body, error) {
-	content, _, diags := proxyConfig.Remain.PartialContent(
-		&hcl.BodySchema{Blocks: []hcl.BlockHeaderSchema{{Type: "websockets"}}},
-	)
-	if diags.HasErrors() {
-		return false, nil, diags
-	}
-
-	if proxyConfig.Websockets != nil && len(content.Blocks.OfType("websockets")) > 0 {
-		return false, nil, fmt.Errorf("either websockets attribute or block is allowed")
+func getWebsocketsConfig(proxyConfig *config.Proxy) (bool, *hclsyntax.Body, error) {
+	hasWebsocketBlocks := len(hclbody.BlocksOfType(proxyConfig.HCLBody(), "websockets")) > 0
+	if proxyConfig.Websockets != nil && hasWebsocketBlocks {
+		hr := proxyConfig.HCLBody().Attributes["websockets"].SrcRange
+		return false, nil, newDiagErr(&hr, "either websockets attribute or block is allowed")
 	}
 
 	if proxyConfig.Websockets != nil {
-		var body hcl.Body
+		var body *hclsyntax.Body
 
 		if *proxyConfig.Websockets {
-			block := &hcl.Block{
+			block := &hclsyntax.Block{
 				Type: "websockets",
-				Body: hclbody.EmptyBody(),
+				Body: &hclsyntax.Body{},
 			}
 
-			body = hclbody.New(&hcl.BodyContent{Blocks: []*hcl.Block{block}})
+			body = &hclsyntax.Body{Blocks: []*hclsyntax.Block{block}}
 		}
 
 		return *proxyConfig.Websockets, body, nil
 	}
 
-	return len(content.Blocks) > 0, nil, nil
+	return hasWebsocketBlocks, nil, nil
 }
diff --git a/config/configload/endpoint_sequence.go b/config/configload/endpoint_sequence.go
index 4383e267..a6d5c546 100644
--- a/config/configload/endpoint_sequence.go
+++ b/config/configload/endpoint_sequence.go
@@ -4,6 +4,7 @@ import (
 	"sort"
 
 	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config"
 	"github.com/avenga/couper/config/body"
@@ -12,7 +13,7 @@ import (
 )
 
 // buildSequences collects possible dependencies from 'backend_responses' variable.
-func buildSequences(names map[string]hcl.Body, endpoint *config.Endpoint) (err error) {
+func buildSequences(names map[string]*hclsyntax.Body, endpoint *config.Endpoint) (err error) {
 	sequences := map[string]*sequence.Item{}
 
 	defer func() {
@@ -64,7 +65,7 @@ func buildSequences(names map[string]hcl.Body, endpoint *config.Endpoint) (err e
 	return err
 }
 
-func responseReferences(b hcl.Body) []string {
+func responseReferences(b *hclsyntax.Body) []string {
 	var result []string
 	unique := map[string]struct{}{}
 
diff --git a/config/configload/error_handler.go b/config/configload/error_handler.go
index 234c846c..f3ff68cc 100644
--- a/config/configload/error_handler.go
+++ b/config/configload/error_handler.go
@@ -6,14 +6,16 @@ import (
 
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config"
+	hclbody "github.com/avenga/couper/config/body"
 	"github.com/avenga/couper/config/configload/collect"
 	"github.com/avenga/couper/errors"
 )
 
 type kindContent struct {
-	body  hcl.Body
+	body  *hclsyntax.Body
 	kinds []string
 }
 
@@ -24,7 +26,7 @@ func configureErrorHandler(setter []collect.ErrorHandlerSetter, helper *helper)
 			continue
 		}
 
-		kinds, ehc, err := newErrorHandlerContent(bodyToContent(body.HCLBody()))
+		kinds, ehc, err := newErrorHandlerContent(body.HCLBody())
 		if err != nil {
 			return err
 		}
@@ -60,7 +62,7 @@ func configureErrorHandler(setter []collect.ErrorHandlerSetter, helper *helper)
 
 // newErrorHandlerContent reads given error_handler block contents and maps them by unique
 // error kind declaration.
-func newErrorHandlerContent(content *hcl.BodyContent) (map[string]struct{}, []kindContent, error) {
+func newErrorHandlerContent(content *hclsyntax.Body) (map[string]struct{}, []kindContent, error) {
 	if content == nil {
 		return nil, nil, fmt.Errorf("empty hcl content")
 	}
@@ -68,14 +70,14 @@ func newErrorHandlerContent(content *hcl.BodyContent) (map[string]struct{}, []ki
 	configuredKinds := make(map[string]struct{})
 	var kindContents []kindContent
 
-	for _, block := range content.Blocks.OfType(errorHandler) {
+	for _, block := range hclbody.BlocksOfType(content, errorHandler) {
 		kinds, err := newKindsFromLabels(block)
 		if err != nil {
 			return nil, nil, err
 		}
 		for _, k := range kinds {
 			if _, exist := configuredKinds[k]; exist {
-				subjRange := block.DefRange
+				subjRange := block.DefRange()
 				if len(block.LabelRanges) > 0 {
 					subjRange = block.LabelRanges[0]
 				}
@@ -88,7 +90,7 @@ func newErrorHandlerContent(content *hcl.BodyContent) (map[string]struct{}, []ki
 			}
 
 			if k != errors.Wildcard && !errors.IsKnown(k) {
-				subjRange := block.DefRange
+				subjRange := block.DefRange()
 				if len(block.LabelRanges) > 0 {
 					subjRange = block.LabelRanges[0]
 				}
@@ -114,7 +116,7 @@ func newErrorHandlerContent(content *hcl.BodyContent) (map[string]struct{}, []ki
 const errorHandlerLabelSep = " "
 
 // newKindsFromLabels reads two possible kind formats and returns them per slice entry.
-func newKindsFromLabels(block *hcl.Block) ([]string, error) {
+func newKindsFromLabels(block *hclsyntax.Block) ([]string, error) {
 	var allKinds []string
 	for _, kinds := range block.Labels {
 		all := strings.Split(kinds, errorHandlerLabelSep)
diff --git a/config/configload/helper.go b/config/configload/helper.go
index 7592b70f..a2bfe4bf 100644
--- a/config/configload/helper.go
+++ b/config/configload/helper.go
@@ -9,6 +9,7 @@ import (
 	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config"
+	hclbody "github.com/avenga/couper/config/body"
 	"github.com/avenga/couper/config/sequence"
 	"github.com/avenga/couper/errors"
 	"github.com/avenga/couper/eval"
@@ -18,7 +19,7 @@ type helper struct {
 	config       *config.Couper
 	context      *hcl.EvalContext
 	content      *hcl.BodyContent
-	defsBackends map[string]hcl.Body
+	defsBackends map[string]*hclsyntax.Body
 }
 
 // newHelper creates a container with some methods to keep things simple here and there.
@@ -47,14 +48,15 @@ func newHelper(body hcl.Body, src [][]byte, environment string) (*helper, error)
 		config:       couperConfig,
 		content:      content,
 		context:      couperConfig.Context.(*eval.Context).HCLContext(),
-		defsBackends: make(map[string]hcl.Body),
+		defsBackends: make(map[string]*hclsyntax.Body),
 	}, nil
 }
 
 func (h *helper) addBackend(block *hcl.Block) {
 	name := block.Labels[0]
 
-	backendBody := newBodyWithName(name, block.Body)
+	backendBody := block.Body.(*hclsyntax.Body)
+	setName(name, backendBody)
 
 	h.defsBackends[name] = backendBody
 }
@@ -95,8 +97,8 @@ func (h *helper) configureACBackends() error {
 	}
 
 	for _, ac := range acs {
-		if err := ac.Prepare(func(attr string, attrVal string, i config.Inline) (hcl.Body, error) {
-			return PrepareBackend(h, attr, attrVal, i) // wrap helper
+		if err := ac.Prepare(func(attr string, attrVal string, b config.Body) (*hclsyntax.Body, error) {
+			return PrepareBackend(h, attr, attrVal, b) // wrap helper
 		}); err != nil {
 			return err
 		}
@@ -163,27 +165,16 @@ func (h *helper) resolveBackendDeps() (uniqueItems []string, err error) {
 func (h *helper) collectBackendDeps(refs map[string][]string) {
 	for name, b := range h.defsBackends {
 		refs[name] = nil
-		content, _, _ := b.PartialContent(&hcl.BodySchema{
-			Blocks: []hcl.BlockHeaderSchema{
-				{Type: oauth2},
-				{Type: tokenRequest, LabelNames: []string{"name"}, LabelOptional: true},
-			}},
-		)
-		oaBlocks := content.Blocks.OfType(oauth2)
+		oaBlocks := hclbody.BlocksOfType(b, oauth2)
 		h.collectFromBlocks(oaBlocks, name, refs)
-		trBlocks := content.Blocks.OfType(tokenRequest)
+		trBlocks := hclbody.BlocksOfType(b, tokenRequest)
 		h.collectFromBlocks(trBlocks, name, refs)
 	}
 }
 
-func (h *helper) collectFromBlocks(authorizerBlocks hcl.Blocks, name string, refs map[string][]string) {
+func (h *helper) collectFromBlocks(authorizerBlocks hclsyntax.Blocks, name string, refs map[string][]string) {
 	for _, ab := range authorizerBlocks {
-		asb, ok := ab.Body.(*hclsyntax.Body)
-		if !ok {
-			continue
-		}
-
-		for _, be := range asb.Attributes {
+		for _, be := range ab.Body.Attributes {
 			if be.Name == backend {
 				val, _ := be.Expr.Value(envContext)
 				refs[name] = append(refs[name], val.AsString())
@@ -191,7 +182,7 @@ func (h *helper) collectFromBlocks(authorizerBlocks hcl.Blocks, name string, ref
 			}
 		}
 
-		for _, block := range asb.Blocks {
+		for _, block := range ab.Body.Blocks {
 			if block.Type != backend {
 				continue
 			}
diff --git a/config/configload/load.go b/config/configload/load.go
index fb179d48..ae4131d0 100644
--- a/config/configload/load.go
+++ b/config/configload/load.go
@@ -14,6 +14,7 @@ import (
 	"github.com/zclconf/go-cty/cty"
 
 	"github.com/avenga/couper/config"
+	hclbody "github.com/avenga/couper/config/body"
 	"github.com/avenga/couper/config/configload/collect"
 	configfile "github.com/avenga/couper/config/configload/file"
 	"github.com/avenga/couper/config/parser"
@@ -242,12 +243,12 @@ func loadTestContents(tcs []testContent) (*config.Couper, error) {
 	)
 
 	for _, tc := range tcs {
-		hclBody, diags := parser.Load(tc.src, tc.filename)
-		if diags.HasErrors() {
-			return nil, diags
+		hclBody, err := parser.Load(tc.src, tc.filename)
+		if err != nil {
+			return nil, err
 		}
 
-		parsedBodies = append(parsedBodies, hclBody.(*hclsyntax.Body))
+		parsedBodies = append(parsedBodies, hclBody)
 		srcs = append(srcs, tc.src)
 	}
 
@@ -255,9 +256,9 @@ func loadTestContents(tcs []testContent) (*config.Couper, error) {
 }
 
 func LoadBytes(src []byte, filename string) (*config.Couper, error) {
-	hclBody, diags := parser.Load(src, filename)
-	if diags.HasErrors() {
-		return nil, diags
+	hclBody, err := parser.Load(src, filename)
+	if err != nil {
+		return nil, err
 	}
 
 	if err := validateBody(hclBody, false); err != nil {
@@ -267,7 +268,7 @@ func LoadBytes(src []byte, filename string) (*config.Couper, error) {
 	return LoadConfig(hclBody, [][]byte{src}, "")
 }
 
-func LoadConfig(body hcl.Body, src [][]byte, environment string) (*config.Couper, error) {
+func LoadConfig(body *hclsyntax.Body, src [][]byte, environment string) (*config.Couper, error) {
 	var err error
 
 	if diags := ValidateConfigSchema(body, &config.Couper{}); diags.HasErrors() {
@@ -380,7 +381,7 @@ func LoadConfig(body hcl.Body, src [][]byte, environment string) (*config.Couper
 		WithSAML(helper.config.Definitions.SAML)
 
 	// Read per server block and merge backend settings which results in a final server configuration.
-	for _, serverBlock := range bodyToContent(body).Blocks.OfType(server) {
+	for _, serverBlock := range hclbody.BlocksOfType(body, server) {
 		serverConfig := &config.Server{}
 		if diags := gohcl.DecodeBody(serverBlock.Body, helper.context, serverConfig); diags.HasErrors() {
 			return nil, diags
@@ -393,15 +394,15 @@ func LoadConfig(body hcl.Body, src [][]byte, environment string) (*config.Couper
 
 		// Read api blocks and merge backends with server and definitions backends.
 		for _, apiConfig := range serverConfig.APIs {
-			apiContent := bodyToContent(apiConfig.Remain)
+			apiBody := apiConfig.HCLBody()
 
 			if apiConfig.AllowedMethods != nil && len(apiConfig.AllowedMethods) > 0 {
-				if err = validMethods(apiConfig.AllowedMethods, &apiContent.Attributes["allowed_methods"].Range); err != nil {
+				if err = validMethods(apiConfig.AllowedMethods, apiBody.Attributes["allowed_methods"]); err != nil {
 					return nil, err
 				}
 			}
 
-			rp := apiContent.Attributes["beta_required_permission"]
+			rp := apiBody.Attributes["beta_required_permission"]
 			if rp != nil {
 				apiConfig.RequiredPermission = rp.Expr
 			}
diff --git a/config/configload/merge.go b/config/configload/merge.go
index 17dc1129..09551dcd 100644
--- a/config/configload/merge.go
+++ b/config/configload/merge.go
@@ -14,7 +14,7 @@ import (
 )
 
 const (
-	errMultipleBackends = "Multiple definitions of backend are not allowed."
+	errMultipleBackends = "Multiple definitions of backend are not allowed in %s."
 	errUniqueLabels     = "All %s blocks must have unique labels."
 )
 
@@ -143,7 +143,7 @@ func mergeServers(bodies []*hclsyntax.Body, proxies map[string]*hclsyntax.Block)
 				uniqueAPILabels, uniqueSPALabels, uniqueFilesLabels := make(map[string]struct{}), make(map[string]struct{}), make(map[string]struct{})
 
 				if block.Type == endpoint {
-					if err := absInBackends(block); err != nil { // Backend block inside a free endpoint block
+					if err := checkForMultipleBackends(block); err != nil { // Backend block inside a free endpoint block
 						return nil, err
 					}
 
@@ -194,7 +194,7 @@ func mergeServers(bodies []*hclsyntax.Body, proxies map[string]*hclsyntax.Block)
 
 					for _, subBlock := range block.Body.Blocks {
 						if subBlock.Type == endpoint {
-							if err := absInBackends(subBlock); err != nil {
+							if err := checkForMultipleBackends(subBlock); err != nil {
 								return nil, err
 							}
 
@@ -208,7 +208,7 @@ func mergeServers(bodies []*hclsyntax.Body, proxies map[string]*hclsyntax.Block)
 
 							results[serverKey].apis[apiKey].endpoints[subBlock.Labels[0]] = subBlock
 						} else if subBlock.Type == errorHandler {
-							if err := absInBackends(subBlock); err != nil {
+							if err := checkForMultipleBackends(subBlock); err != nil {
 								return nil, err
 							}
 
@@ -424,6 +424,12 @@ func mergeDefinitions(bodies []*hclsyntax.Body) (*hclsyntax.Block, map[string]*h
 						definitionsBlock[innerBlock.Type] = make(data)
 					}
 
+					if innerBlock.Type == backend {
+						if err := checkForMultipleBackendsInBackend(innerBlock); err != nil {
+							return nil, nil, err
+						}
+					}
+
 					// Count the "backend" blocks and "backend" attributes to
 					// forbid multiple backend definitions.
 
@@ -431,11 +437,14 @@ func mergeDefinitions(bodies []*hclsyntax.Body) (*hclsyntax.Block, map[string]*h
 
 					for _, block := range innerBlock.Body.Blocks {
 						if block.Type == errorHandler {
-							if err := absInBackends(block); err != nil {
+							if err := checkForMultipleBackends(block); err != nil {
 								return nil, nil, err
 							}
 						} else if block.Type == backend {
 							backends++
+							if err := checkForMultipleBackendsInBackend(block); err != nil {
+								return nil, nil, err
+							}
 						}
 					}
 
@@ -583,13 +592,13 @@ func absPath(attr *hclsyntax.Attribute) hclsyntax.Expression {
 	}
 }
 
-// absInBackends searches for "backend" blocks inside a proxy or request block to
+// checkForMultipleBackends searches for "backend" blocks inside a proxy or request block to
 // count the "backend"
 // blocks and "backend" attributes to forbid multiple backend definitions.
-func absInBackends(block *hclsyntax.Block) error {
+func checkForMultipleBackends(block *hclsyntax.Block) error {
 	for _, subBlock := range block.Body.Blocks {
 		if subBlock.Type == errorHandler {
-			return absInBackends(subBlock) // Recursive call
+			return checkForMultipleBackends(subBlock) // Recursive call
 		}
 
 		if subBlock.Type != proxy && subBlock.Type != request {
@@ -601,6 +610,38 @@ func absInBackends(block *hclsyntax.Block) error {
 		for _, subSubBlock := range subBlock.Body.Blocks {
 			if subSubBlock.Type == backend {
 				backends++
+				if err := checkForMultipleBackendsInBackend(subSubBlock); err != nil {
+					return err
+				}
+			}
+		}
+
+		if _, ok := subBlock.Body.Attributes[backend]; ok {
+			backends++
+		}
+
+		if backends > 1 {
+			return newMergeError(errMultipleBackends, subBlock)
+		}
+	}
+
+	return nil
+}
+
+func checkForMultipleBackendsInBackend(block *hclsyntax.Block) error {
+	for _, subBlock := range block.Body.Blocks {
+		if subBlock.Type != oauth2 && subBlock.Type != tokenRequest {
+			continue
+		}
+
+		var backends int
+
+		for _, subSubBlock := range subBlock.Body.Blocks {
+			if subSubBlock.Type == backend {
+				backends++
+				if err := checkForMultipleBackendsInBackend(subSubBlock); err != nil { // Recursive call
+					return err
+				}
 			}
 		}
 
@@ -609,7 +650,7 @@ func absInBackends(block *hclsyntax.Block) error {
 		}
 
 		if backends > 1 {
-			return newMergeError(errMultipleBackends, block)
+			return newMergeError(errMultipleBackends, subBlock)
 		}
 	}
 
diff --git a/config/configload/schema.go b/config/configload/schema.go
index 4afadf89..ec24d3df 100644
--- a/config/configload/schema.go
+++ b/config/configload/schema.go
@@ -6,14 +6,15 @@ import (
 	"regexp"
 	"strings"
 
-	"github.com/avenga/couper/config"
-	"github.com/avenga/couper/config/configload/collect"
-	"github.com/avenga/couper/config/meta"
-	"github.com/avenga/couper/internal/seetie"
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
 	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/zclconf/go-cty/cty"
+
+	"github.com/avenga/couper/config"
+	"github.com/avenga/couper/config/configload/collect"
+	"github.com/avenga/couper/config/meta"
+	"github.com/avenga/couper/internal/seetie"
 )
 
 const (
@@ -179,7 +180,7 @@ func completeSchemaComponents(body hcl.Body, schema *hcl.BodySchema,
 	for _, diag := range diags {
 		// TODO: How to implement this block automatically?
 		if diag.Detail == noLabelForErrorHandler {
-			bodyContent := bodyToContent(body)
+			bodyContent := bodyToContent(body.(*hclsyntax.Body))
 
 			for _, block := range bodyContent.Blocks {
 				if block.Type == errorHandler {
@@ -259,13 +260,9 @@ func uniqueErrors(errors hcl.Diagnostics) hcl.Diagnostics {
 	return unique
 }
 
-func bodyToContent(body hcl.Body) *hcl.BodyContent {
+func bodyToContent(b *hclsyntax.Body) *hcl.BodyContent {
 	content := &hcl.BodyContent{
-		MissingItemRange: *getRange(body),
-	}
-	b, ok := body.(*hclsyntax.Body)
-	if !ok {
-		return content
+		MissingItemRange: *getRange(b),
 	}
 
 	if len(b.Attributes) > 0 {
@@ -294,15 +291,10 @@ func bodyToContent(body hcl.Body) *hcl.BodyContent {
 	return content
 }
 
-func getRange(body hcl.Body) *hcl.Range {
+func getRange(body *hclsyntax.Body) *hcl.Range {
 	if body == nil {
 		return &hcl.Range{}
 	}
 
-	if b, ok := body.(*hclsyntax.Body); ok {
-		return &b.SrcRange
-	}
-
-	r := body.MissingItemRange()
-	return &r
+	return &body.SrcRange
 }
diff --git a/config/configload/validate.go b/config/configload/validate.go
index 22a9adf3..71afbdb7 100644
--- a/config/configload/validate.go
+++ b/config/configload/validate.go
@@ -10,7 +10,6 @@ import (
 	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/zclconf/go-cty/cty"
 
-	"github.com/avenga/couper/config"
 	"github.com/avenga/couper/eval"
 	"github.com/avenga/couper/utils"
 )
@@ -32,13 +31,8 @@ func newDiagErr(subject *hcl.Range, summary string) error {
 	}}
 }
 
-func validateBody(body hcl.Body, afterMerge bool) error {
-	hsBody, ok := body.(*hclsyntax.Body)
-	if !ok {
-		return fmt.Errorf("body must be hclsyntax.Body")
-	}
-
-	for _, outerBlock := range hsBody.Blocks {
+func validateBody(body *hclsyntax.Body, afterMerge bool) error {
+	for _, outerBlock := range body.Blocks {
 		if outerBlock.Type == definitions {
 			uniqueBackends := make(map[string]struct{})
 			uniqueACs := make(map[string]struct{})
@@ -240,10 +234,10 @@ func uniqueLabelName(scopeOfUniqueness string, unique map[string]struct{}, name
 	return nil
 }
 
-func verifyBodyAttributes(blockName string, content *hcl.BodyContent) error {
-	_, existsBody := content.Attributes["body"]
-	_, existsFormBody := content.Attributes["form_body"]
-	_, existsJSONBody := content.Attributes["json_body"]
+func verifyBodyAttributes(blockName string, body *hclsyntax.Body) error {
+	_, existsBody := body.Attributes["body"]
+	_, existsFormBody := body.Attributes["form_body"]
+	_, existsJSONBody := body.Attributes["json_body"]
 
 	if existsBody && existsFormBody || existsBody && existsJSONBody || existsFormBody && existsJSONBody {
 		rangeAttr := "body"
@@ -251,81 +245,62 @@ func verifyBodyAttributes(blockName string, content *hcl.BodyContent) error {
 			rangeAttr = "form_body"
 		}
 
-		return newDiagErr(&content.Attributes[rangeAttr].Range,
+		r := body.Attributes[rangeAttr].Range()
+		return newDiagErr(&r,
 			blockName+" can only have one of body, form_body or json_body attributes")
 	}
 
 	return nil
 }
 
-func verifyResponseBodyAttrs(b hcl.Body) error {
-	content, _, _ := b.PartialContent(config.ResponseInlineSchema)
-	_, existsBody := content.Attributes["body"]
-	_, existsJSONBody := content.Attributes["json_body"]
+func verifyResponseBodyAttrs(b *hclsyntax.Body) error {
+	_, existsBody := b.Attributes["body"]
+	_, existsJSONBody := b.Attributes["json_body"]
 	if existsBody && existsJSONBody {
-		return newDiagErr(&content.Attributes["body"].Range, "response can only have one of body or json_body attributes")
+		r := b.Attributes["body"].Range()
+		return newDiagErr(&r, "response can only have one of body or json_body attributes")
 	}
 	return nil
 }
 
 var invalidAttributes = []string{"disable_certificate_validation", "disable_connection_reuse", "http2", "max_connections"}
-var forbiddenInRefinedBackendBlockSchema = &hcl.BodySchema{
-	Blocks: []hcl.BlockHeaderSchema{
-		{
-			Type: "openapi",
-		},
-		config.OAuthBlockHeaderSchema,
-		{
-			Type: "beta_health",
-		},
-		config.TokenRequestBlockHeaderSchema,
-		{
-			Type: "beta_rate_limit",
-		},
-	},
-}
 
-func invalidRefinement(body hcl.Body) error {
+func invalidRefinement(body *hclsyntax.Body) error {
 	const message = "backend reference: refinement for %q is not permitted"
-	attrs, _ := body.JustAttributes()
-	if attrs == nil {
-		return nil
-	}
 	for _, name := range invalidAttributes {
-		attr, exist := attrs[name]
+		attr, exist := body.Attributes[name]
 		if exist {
 			return newDiagErr(&attr.NameRange, fmt.Sprintf(message, attr.Name))
 		}
 	}
 
-	content, _, _ := body.PartialContent(forbiddenInRefinedBackendBlockSchema)
-	if content != nil && len(content.Blocks) > 0 {
-		return newDiagErr(&content.Blocks[0].DefRange, fmt.Sprintf(message, content.Blocks[0].Type))
+	if len(body.Blocks) > 0 {
+		r := body.Blocks[0].DefRange()
+		return newDiagErr(&r, fmt.Sprintf(message, body.Blocks[0].Type))
 	}
 
 	return nil
 }
 
-func invalidOriginRefinement(reference, params hcl.Body) error {
+func invalidOriginRefinement(reference, params *hclsyntax.Body) error {
 	const origin = "origin"
-	refAttrs, _ := reference.JustAttributes()
-	paramAttrs, _ := params.JustAttributes()
-
-	refOrigin := refAttrs[origin]
-	paramOrigin := paramAttrs[origin]
+	refOrigin := reference.Attributes[origin]
+	paramOrigin := params.Attributes[origin]
 
 	if paramOrigin != nil && refOrigin != nil {
 		if paramOrigin.Expr != refOrigin.Expr {
-			return newDiagErr(&paramOrigin.Range, "backend reference: origin must be equal")
+			r := paramOrigin.Range()
+			return newDiagErr(&r, "backend reference: origin must be equal")
 		}
 	}
 	return nil
 }
 
-func validMethods(methods []string, hr *hcl.Range) error {
+func validMethods(methods []string, attr *hclsyntax.Attribute) error {
 	for _, method := range methods {
 		if !methodRegExp.MatchString(method) {
-			return newDiagErr(hr, "method contains invalid character(s)")
+			r := attr.Range()
+			return newDiagErr(&r, "method contains invalid character(s)")
 		}
 	}
 
diff --git a/config/endpoint.go b/config/endpoint.go
index 2a80e1a1..701e8f8b 100644
--- a/config/endpoint.go
+++ b/config/endpoint.go
@@ -3,12 +3,16 @@ package config
 import (
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config/meta"
 	"github.com/avenga/couper/config/sequence"
 )
 
-var _ Inline = &Endpoint{}
+var (
+	_ Body   = &Endpoint{}
+	_ Inline = &Endpoint{}
+)
 
 // Endpoint represents the <Endpoint> object.
 type Endpoint struct {
@@ -33,9 +37,9 @@ type Endpoint struct {
 // Endpoints represents a list of <Endpoint> objects.
 type Endpoints []*Endpoint
 
-// HCLBody implements the <Inline> interface.
-func (e Endpoint) HCLBody() hcl.Body {
-	return e.Remain
+// HCLBody implements the <Body> interface.
+func (e Endpoint) HCLBody() *hclsyntax.Body {
+	return e.Remain.(*hclsyntax.Body)
 }
 
 // Inline implements the <Inline> interface.
diff --git a/config/error_handler.go b/config/error_handler.go
index 4432a10b..a4b4f996 100644
--- a/config/error_handler.go
+++ b/config/error_handler.go
@@ -3,11 +3,15 @@ package config
 import (
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config/meta"
 )
 
-var _ Inline = &ErrorHandler{}
+var (
+	_ Body   = &ErrorHandler{}
+	_ Inline = &ErrorHandler{}
+)
 
 // ErrorHandler represents a subset of Endpoint.
 type ErrorHandler struct {
@@ -24,9 +28,9 @@ type ErrorHandlerGetter interface {
 	DefaultErrorHandler() *ErrorHandler
 }
 
-// HCLBody implements the <Inline> interface.
-func (e ErrorHandler) HCLBody() hcl.Body {
-	return e.Remain
+// HCLBody implements the <Body> interface.
+func (e ErrorHandler) HCLBody() *hclsyntax.Body {
+	return e.Remain.(*hclsyntax.Body)
 }
 
 // Inline implements the <Inline> interface.
diff --git a/config/files.go b/config/files.go
index bec7f4ea..ea5c3e65 100644
--- a/config/files.go
+++ b/config/files.go
@@ -23,11 +23,6 @@ type Files struct {
 	Remain               hcl.Body `hcl:",remain"`
 }
 
-// HCLBody implements the <Inline> interface.
-func (f Files) HCLBody() hcl.Body {
-	return f.Remain
-}
-
 // Inline implements the <Inline> interface.
 func (f Files) Inline() interface{} {
 	type Inline struct {
diff --git a/config/inline.go b/config/inline.go
index 5e1d974c..17a0ce32 100644
--- a/config/inline.go
+++ b/config/inline.go
@@ -1,10 +1,12 @@
 package config
 
-import "github.com/hashicorp/hcl/v2"
+import (
+	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
+)
 
 // Inline defines the <Inline> interface.
 type Inline interface {
-	Body
 	Inline() interface{}
 	Schema(inline bool) *hcl.BodySchema
 }
@@ -14,7 +16,7 @@ type BackendReference interface {
 	Reference() string
 }
 
-type PrepareBackendFunc func(attr string, val string, body Inline) (hcl.Body, error)
+type PrepareBackendFunc func(attr string, val string, body Body) (*hclsyntax.Body, error)
 
 type BackendInitialization interface {
 	Prepare(backendFunc PrepareBackendFunc) error
@@ -22,5 +24,5 @@ type BackendInitialization interface {
 
 // Body defines the <Body> interface.
 type Body interface {
-	HCLBody() hcl.Body
+	HCLBody() *hclsyntax.Body
 }
diff --git a/config/oauth2.go b/config/oauth2.go
index a6a4bb35..80bc3337 100644
--- a/config/oauth2.go
+++ b/config/oauth2.go
@@ -11,7 +11,6 @@ type OAuth2AS interface {
 
 // OAuth2Client represents the client configuration for OAuth2 clients.
 type OAuth2Client interface {
-	Inline
 	ClientAuthenticationRequired() bool
 	GetClientID() string
 	GetClientSecret() string
@@ -20,6 +19,7 @@ type OAuth2Client interface {
 
 // OAuth2AcClient represents the client configuration for OAuth2 clients using the authorization code flow.
 type OAuth2AcClient interface {
+	Body
 	OAuth2Client
 	GetGrantType() string
 	GetRedirectURI() string
@@ -29,7 +29,6 @@ type OAuth2AcClient interface {
 
 // OAuth2Authorization represents the configuration for the OAuth2 authorization URL function
 type OAuth2Authorization interface {
-	Inline
 	GetAuthorizationEndpoint() (string, error)
 	GetClientID() string
 	GetRedirectURI() string
diff --git a/config/oauth2ra.go b/config/oauth2ra.go
index ae4d045d..8e6161ee 100644
--- a/config/oauth2ra.go
+++ b/config/oauth2ra.go
@@ -3,6 +3,7 @@ package config
 import (
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 )
 
 const (
@@ -11,17 +12,18 @@ const (
 	Password          = "password"
 )
 
-var OAuthBlockHeaderSchema = hcl.BlockHeaderSchema{
+var oauthBlockHeaderSchema = hcl.BlockHeaderSchema{
 	Type: "oauth2",
 }
 var OAuthBlockSchema = &hcl.BodySchema{
 	Blocks: []hcl.BlockHeaderSchema{
-		OAuthBlockHeaderSchema,
+		oauthBlockHeaderSchema,
 	},
 }
 
 var (
 	_ BackendReference = &OAuth2ReqAuth{}
+	_ Body             = &OAuth2ReqAuth{}
 	_ Inline           = &OAuth2ReqAuth{}
 	_ OAuth2Client     = &OAuth2ReqAuth{}
 	_ OAuth2AS         = &OAuth2ReqAuth{}
@@ -48,9 +50,9 @@ func (oa *OAuth2ReqAuth) Reference() string {
 	return oa.BackendName
 }
 
-// HCLBody implements the <Inline> interface.
-func (oa *OAuth2ReqAuth) HCLBody() hcl.Body {
-	return oa.Remain
+// HCLBody implements the <Body> interface.
+func (oa *OAuth2ReqAuth) HCLBody() *hclsyntax.Body {
+	return oa.Remain.(*hclsyntax.Body)
 }
 
 // Inline implements the <Inline> interface.
@@ -71,11 +73,6 @@ func (oa *OAuth2ReqAuth) Schema(inline bool) *hcl.BodySchema {
 
 	schema, _ := gohcl.ImpliedBodySchema(oa.Inline())
 
-	// A backend reference is defined, backend block is not allowed.
-	if oa.BackendName != "" {
-		schema.Blocks = nil
-	}
-
 	return schema
 }
 
diff --git a/config/parser/load.go b/config/parser/load.go
index c43f72b5..aa11b502 100644
--- a/config/parser/load.go
+++ b/config/parser/load.go
@@ -1,26 +1,23 @@
 package parser
 
 import (
-	"strings"
+	"fmt"
 
-	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/hclparse"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 )
 
-func Load(src []byte, name string) (hcl.Body, hcl.Diagnostics) {
+func Load(src []byte, name string) (*hclsyntax.Body, error) {
 	parser := hclparse.NewParser()
 
-	var file *hcl.File
-	var diags hcl.Diagnostics
-
-	if strings.HasSuffix(name, ".json") {
-		file, diags = parser.ParseJSON(src, name)
-	} else {
-		file, diags = parser.ParseHCL(src, name)
+	file, diags := parser.ParseHCL(src, name)
+	if file == nil || file.Body == nil {
+		return nil, diags
 	}
 
-	if file == nil || file.Body == nil {
-		return hcl.EmptyBody(), diags
+	hsbody, ok := file.Body.(*hclsyntax.Body)
+	if !ok {
+		return nil, fmt.Errorf("couper configuration must be in native HCL syntax")
 	}
-	return file.Body, diags
+	return hsbody, nil
 }
diff --git a/config/proxy.go b/config/proxy.go
index 5c597d21..531e523b 100644
--- a/config/proxy.go
+++ b/config/proxy.go
@@ -3,12 +3,14 @@ package config
 import (
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config/meta"
 )
 
 var (
 	_ BackendReference = &Proxy{}
+	_ Body             = &Proxy{}
 	_ Inline           = &Proxy{}
 )
 
@@ -21,7 +23,7 @@ type Proxy struct {
 	Websockets  *bool    `hcl:"websockets,optional" docs:"Allows support for WebSockets. This attribute is only allowed in the \"default\" proxy block. Other {proxy} blocks, {request} blocks or {response} blocks are not allowed within the current {endpoint} block."`
 
 	// internally used
-	Backend hcl.Body
+	Backend *hclsyntax.Body
 }
 
 // Proxies represents a list of <Proxy> objects.
@@ -32,9 +34,9 @@ func (p Proxy) Reference() string {
 	return p.BackendName
 }
 
-// HCLBody implements the <Inline> interface.
-func (p Proxy) HCLBody() hcl.Body {
-	return p.Remain
+// HCLBody implements the <Body> interface.
+func (p Proxy) HCLBody() *hclsyntax.Body {
+	return p.Remain.(*hclsyntax.Body)
 }
 
 // Inline implements the <Inline> interface.
@@ -61,29 +63,6 @@ func (p Proxy) Schema(inline bool) *hcl.BodySchema {
 	}
 
 	schema, _ := gohcl.ImpliedBodySchema(p.Inline())
-	backup := schema.Blocks[:]
-	schema.Blocks = nil
-
-	if p.BackendName == "" {
-		var blocks []hcl.BlockHeaderSchema
-
-		for _, block := range backup {
-			if block.Type == "backend" {
-				blocks = append(blocks, block)
-			}
-		}
-
-		schema.Blocks = blocks
-	}
-
-	if p.Websockets == nil {
-		for _, block := range backup {
-			if block.Type == "websockets" {
-				// No websockets flag is set, websocket block is allowed.
-				schema.Blocks = append(schema.Blocks, block)
-			}
-		}
-	}
 
 	return meta.MergeSchemas(schema, meta.ModifierAttributesSchema)
 }
diff --git a/config/request.go b/config/request.go
index b0273451..e727486e 100644
--- a/config/request.go
+++ b/config/request.go
@@ -3,11 +3,13 @@ package config
 import (
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/zclconf/go-cty/cty"
 )
 
 var (
 	_ BackendReference = &Request{}
+	_ Body             = &Request{}
 	_ Inline           = &Request{}
 )
 
@@ -18,7 +20,7 @@ type Request struct {
 	Remain      hcl.Body `hcl:",remain"`
 
 	// Internally used
-	Backend hcl.Body
+	Backend *hclsyntax.Body
 }
 
 // Requests represents a list of <Requests> objects.
@@ -29,9 +31,9 @@ func (r Request) Reference() string {
 	return r.BackendName
 }
 
-// HCLBody implements the <Inline> interface.
-func (r Request) HCLBody() hcl.Body {
-	return r.Remain
+// HCLBody implements the <Body> interface.
+func (r Request) HCLBody() *hclsyntax.Body {
+	return r.Remain.(*hclsyntax.Body)
 }
 
 // Inline implements the <Inline> interface.
@@ -60,10 +62,5 @@ func (r Request) Schema(inline bool) *hcl.BodySchema {
 
 	schema, _ := gohcl.ImpliedBodySchema(r.Inline())
 
-	// A backend reference is defined, backend block is not allowed.
-	if r.BackendName != "" {
-		schema.Blocks = nil
-	}
-
 	return schema
 }
diff --git a/config/response.go b/config/response.go
index c918515f..c5704db2 100644
--- a/config/response.go
+++ b/config/response.go
@@ -3,9 +3,11 @@ package config
 import (
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 )
 
 var (
+	_ Body   = &Response{}
 	_ Inline = &Response{}
 
 	ResponseInlineSchema = Response{}.Schema(true)
@@ -16,9 +18,9 @@ type Response struct {
 	Remain hcl.Body `hcl:",remain"`
 }
 
-// HCLBody implements the <Inline> interface.
-func (r Response) HCLBody() hcl.Body {
-	return r.Remain
+// HCLBody implements the <Body> interface.
+func (r Response) HCLBody() *hclsyntax.Body {
+	return r.Remain.(*hclsyntax.Body)
 }
 
 // Inline implements the <Inline> interface.
diff --git a/config/runtime/backend.go b/config/runtime/backend.go
index 04941576..ffe7a07b 100644
--- a/config/runtime/backend.go
+++ b/config/runtime/backend.go
@@ -8,12 +8,14 @@ import (
 
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/sirupsen/logrus"
 	"github.com/zclconf/go-cty/cty"
 
 	"github.com/avenga/couper/backend"
 	"github.com/avenga/couper/cache"
 	"github.com/avenga/couper/config"
+	hclbody "github.com/avenga/couper/config/body"
 	"github.com/avenga/couper/errors"
 	"github.com/avenga/couper/eval"
 	"github.com/avenga/couper/handler/producer"
@@ -22,7 +24,7 @@ import (
 	"github.com/avenga/couper/handler/validation"
 )
 
-func NewBackend(ctx *hcl.EvalContext, body hcl.Body, log *logrus.Entry,
+func NewBackend(ctx *hcl.EvalContext, body *hclsyntax.Body, log *logrus.Entry,
 	conf *config.Couper, store *cache.MemoryStore) (http.RoundTripper, error) {
 	const prefix = "backend_"
 	name, err := getBackendName(ctx, body)
@@ -49,7 +51,7 @@ func NewBackend(ctx *hcl.EvalContext, body hcl.Body, log *logrus.Entry,
 	return b.(http.RoundTripper), nil
 }
 
-func newBackend(evalCtx *hcl.EvalContext, backendCtx hcl.Body, log *logrus.Entry,
+func newBackend(evalCtx *hcl.EvalContext, backendCtx *hclsyntax.Body, log *logrus.Entry,
 	conf *config.Couper, memStore *cache.MemoryStore) (http.RoundTripper, error) {
 	beConf := &config.Backend{}
 	if diags := gohcl.DecodeBody(backendCtx, evalCtx, beConf); diags.HasErrors() {
@@ -113,16 +115,11 @@ func newBackend(evalCtx *hcl.EvalContext, backendCtx hcl.Body, log *logrus.Entry
 		}
 	}
 
-	for _, schema := range []*hcl.BodySchema{
-		config.OAuthBlockSchema,
-		config.TokenRequestBlockSchema,
+	for _, blockType := range []string{
+		config.OAuthBlockSchema.Blocks[0].Type,
+		config.TokenRequestBlockSchema.Blocks[0].Type,
 	} {
-		content, _, _ := backendCtx.PartialContent(schema)
-		if content == nil || len(schema.Blocks) == 0 {
-			continue
-		}
-
-		blocks := content.Blocks.OfType(schema.Blocks[0].Type)
+		blocks := hclbody.BlocksOfType(backendCtx, blockType)
 		for _, block := range blocks {
 			requestAuthorizer, err := newRequestAuthorizer(evalCtx, block, log, conf, memStore)
 			if err != nil {
@@ -136,7 +133,7 @@ func newBackend(evalCtx *hcl.EvalContext, backendCtx hcl.Body, log *logrus.Entry
 	return b, nil
 }
 
-func newRequestAuthorizer(evalCtx *hcl.EvalContext, block *hcl.Block,
+func newRequestAuthorizer(evalCtx *hcl.EvalContext, block *hclsyntax.Block,
 	log *logrus.Entry, conf *config.Couper, memStore *cache.MemoryStore) (transport.RequestAuthorizer, error) {
 	var authorizerConfig interface{}
 	switch block.Type {
@@ -146,11 +143,8 @@ func newRequestAuthorizer(evalCtx *hcl.EvalContext, block *hcl.Block,
 			Retries: &one,
 		}
 	case config.TokenRequestBlockSchema.Blocks[0].Type:
-		label := "default"
-		if len(block.Labels) > 0 {
-			label = block.Labels[0]
-		}
-		authorizerConfig = &config.TokenRequest{Name: label}
+		// block is guaranteed to have a label ("default" being added at configload)
+		authorizerConfig = &config.TokenRequest{Name: block.Labels[0]}
 	default:
 		return nil, errors.Configuration.Messagef("request authorizer not implemented: %s", block.Type)
 	}
@@ -159,15 +153,9 @@ func newRequestAuthorizer(evalCtx *hcl.EvalContext, block *hcl.Block,
 		return nil, diags
 	}
 
-	inlineSchema, _ := authorizerConfig.(config.Inline)
-	innerContent, _, diags := inlineSchema.HCLBody().PartialContent(inlineSchema.Schema(true))
-	if diags.HasErrors() {
-		return nil, diags
-	}
-
-	backendBlocks := innerContent.Blocks.OfType("backend")
+	backendBlocks := hclbody.BlocksOfType(block.Body, "backend")
 	if len(backendBlocks) == 0 {
-		r := inlineSchema.HCLBody().MissingItemRange()
+		r := block.Body.MissingItemRange()
 		diag := &hcl.Diagnostics{&hcl.Diagnostic{
 			Subject: &r,
 			Summary: "missing backend initialization",
@@ -196,20 +184,14 @@ func newRequestAuthorizer(evalCtx *hcl.EvalContext, block *hcl.Block,
 	}
 }
 
-func getBackendName(evalCtx *hcl.EvalContext, backendCtx hcl.Body) (string, error) {
-	content, _, _ := backendCtx.PartialContent(&hcl.BodySchema{Attributes: []hcl.AttributeSchema{
-		{Name: "name"}},
-	})
-
-	if content != nil && len(content.Attributes) > 0 {
-		if n, exist := content.Attributes["name"]; exist {
-			v, err := eval.Value(evalCtx, n.Expr)
-			if err != nil {
-				return "", err
-			}
-
-			return v.AsString(), nil
+func getBackendName(evalCtx *hcl.EvalContext, backendCtx *hclsyntax.Body) (string, error) {
+	if n, exist := backendCtx.Attributes["name"]; exist {
+		v, err := eval.Value(evalCtx, n.Expr)
+		if err != nil {
+			return "", err
 		}
+
+		return v.AsString(), nil
 	}
 
 	return "", nil
diff --git a/config/runtime/endpoint.go b/config/runtime/endpoint.go
index d6b7ae8e..3673f284 100644
--- a/config/runtime/endpoint.go
+++ b/config/runtime/endpoint.go
@@ -107,7 +107,7 @@ func newEndpointOptions(confCtx *hcl.EvalContext, endpointConf *config.Endpoint,
 
 	if endpointConf.Response != nil {
 		response = &producer.Response{
-			Context: endpointConf.Response.Remain,
+			Context: endpointConf.Response.HCLBody(),
 		}
 		blockBodies = append(blockBodies, response.Context)
 	}
@@ -138,7 +138,7 @@ func newEndpointOptions(confCtx *hcl.EvalContext, endpointConf *config.Endpoint,
 
 		pr := &producer.Request{
 			Backend: backend,
-			Context: requestConf.Remain,
+			Context: requestConf.HCLBody(),
 			Name:    requestConf.Name,
 		}
 
@@ -178,7 +178,7 @@ func newEndpointOptions(confCtx *hcl.EvalContext, endpointConf *config.Endpoint,
 
 	return &handler.EndpointOptions{
 		APIName:       apiName,
-		Context:       endpointConf.Remain,
+		Context:       endpointConf.HCLBody(),
 		ErrorTemplate: errTpl,
 		LogPattern:    endpointConf.Pattern,
 		Proxies:       proxies,
diff --git a/config/runtime/error_handler.go b/config/runtime/error_handler.go
index 2b480431..cf28125f 100644
--- a/config/runtime/error_handler.go
+++ b/config/runtime/error_handler.go
@@ -5,6 +5,7 @@ import (
 	"reflect"
 
 	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/sirupsen/logrus"
 
 	"github.com/avenga/couper/config"
@@ -57,7 +58,7 @@ func newErrorHandler(ctx *hcl.EvalContext, conf *config.Couper, opts *protectedO
 				Response:  h.Response,
 			}
 
-			emptyBody := hcl.EmptyBody()
+			emptyBody := &hclsyntax.Body{}
 			if epConf.Response == nil { // Set dummy resp to skip related requirement checks, allowed for error_handler.
 				epConf.Response = &config.Response{Remain: emptyBody}
 			}
diff --git a/config/runtime/server.go b/config/runtime/server.go
index a8bc9ea2..b22c0d0a 100644
--- a/config/runtime/server.go
+++ b/config/runtime/server.go
@@ -13,7 +13,6 @@ import (
 
 	"github.com/docker/go-units"
 	"github.com/hashicorp/hcl/v2"
-	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/sirupsen/logrus"
 
 	ac "github.com/avenga/couper/accesscontrol"
@@ -187,7 +186,7 @@ func NewServerConfiguration(conf *config.Couper, log *logrus.Entry, memStore *ca
 				err = setRoutesFromHosts(serverConfiguration, portsHosts,
 					path.Join(serverOptions.SrvBasePath, spaConf.BasePath, spaPath), spaHandler, spa)
 				if err != nil {
-					sbody := spaConf.HCLBody().(*hclsyntax.Body)
+					sbody := spaConf.HCLBody()
 					return nil, hcl.Diagnostics{&hcl.Diagnostic{
 						Subject: &sbody.Attributes["paths"].SrcRange,
 						Summary: err.Error(),
diff --git a/config/server.go b/config/server.go
index 2f637a7a..1a6b16cb 100644
--- a/config/server.go
+++ b/config/server.go
@@ -28,11 +28,6 @@ type Server struct {
 // Servers represents a list of <Server> objects.
 type Servers []*Server
 
-// HCLBody implements the <Inline> interface.
-func (s Server) HCLBody() hcl.Body {
-	return s.Remain
-}
-
 // Inline implements the <Inline> interface.
 func (s Server) Inline() interface{} {
 	type Inline struct {
diff --git a/config/spa.go b/config/spa.go
index b07cb887..949f8d26 100644
--- a/config/spa.go
+++ b/config/spa.go
@@ -3,11 +3,15 @@ package config
 import (
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config/meta"
 )
 
-var _ Inline = &Spa{}
+var (
+	_ Body   = &Spa{}
+	_ Inline = &Spa{}
+)
 
 type SPAs []*Spa
 
@@ -23,9 +27,9 @@ type Spa struct {
 	Remain               hcl.Body `hcl:",remain"`
 }
 
-// HCLBody implements the <Inline> interface.
-func (s Spa) HCLBody() hcl.Body {
-	return s.Remain
+// HCLBody implements the <Body> interface.
+func (s Spa) HCLBody() *hclsyntax.Body {
+	return s.Remain.(*hclsyntax.Body)
 }
 
 // Inline implements the <Inline> interface.
diff --git a/config/token_request.go b/config/token_request.go
index d56f8e60..ad3dd32f 100644
--- a/config/token_request.go
+++ b/config/token_request.go
@@ -3,22 +3,24 @@ package config
 import (
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/zclconf/go-cty/cty"
 )
 
 var (
 	_ BackendReference = &TokenRequest{}
+	_ Body             = &TokenRequest{}
 	_ Inline           = &TokenRequest{}
 )
 
-var TokenRequestBlockHeaderSchema = hcl.BlockHeaderSchema{
+var tokenRequestBlockHeaderSchema = hcl.BlockHeaderSchema{
 	Type:          "beta_token_request",
 	LabelNames:    []string{"name"},
 	LabelOptional: true,
 }
 var TokenRequestBlockSchema = &hcl.BodySchema{
 	Blocks: []hcl.BlockHeaderSchema{
-		TokenRequestBlockHeaderSchema,
+		tokenRequestBlockHeaderSchema,
 	},
 }
 
@@ -37,9 +39,9 @@ func (t *TokenRequest) Reference() string {
 	return t.BackendName
 }
 
-// HCLBody implements the <Inline> interface.
-func (t *TokenRequest) HCLBody() hcl.Body {
-	return t.Remain
+// HCLBody implements the <Body> interface.
+func (t *TokenRequest) HCLBody() *hclsyntax.Body {
+	return t.Remain.(*hclsyntax.Body)
 }
 
 // Inline implements the <Inline> interface.
@@ -69,10 +71,5 @@ func (t *TokenRequest) Schema(inline bool) *hcl.BodySchema {
 
 	schema, _ := gohcl.ImpliedBodySchema(t.Inline())
 
-	// A backend reference is defined, backend block is not allowed.
-	if t.BackendName != "" {
-		schema.Blocks = nil
-	}
-
 	return schema
 }
diff --git a/config/websockets.go b/config/websockets.go
index 87c9fd92..2864c127 100644
--- a/config/websockets.go
+++ b/config/websockets.go
@@ -17,11 +17,6 @@ type Websockets struct {
 	Remain hcl.Body `hcl:",remain"`
 }
 
-// HCLBody implements the <Inline> interface.
-func (w Websockets) HCLBody() hcl.Body {
-	return w.Remain
-}
-
 // Inline implements the <Inline> interface.
 func (w Websockets) Inline() interface{} {
 	type Inline struct {
diff --git a/eval/buffer.go b/eval/buffer.go
index 54ff9156..125a4610 100644
--- a/eval/buffer.go
+++ b/eval/buffer.go
@@ -7,6 +7,7 @@ import (
 	"strings"
 
 	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config/body"
 )
@@ -45,7 +46,7 @@ func MustBuffer(bodies ...hcl.Body) BufferOption {
 		return result
 	}
 
-	var allExprs []hcl.Expression
+	var allExprs []hclsyntax.Expression
 	allAttributes := body.CollectAttributes(bodies...)
 	allBlockTypes := body.CollectBlockTypes(bodies...)
 
diff --git a/eval/http.go b/eval/http.go
index 35ea1650..9d408c81 100644
--- a/eval/http.go
+++ b/eval/http.go
@@ -10,11 +10,11 @@ import (
 
 	"github.com/docker/go-units"
 	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/sirupsen/logrus"
 	"github.com/zclconf/go-cty/cty"
 	"github.com/zclconf/go-cty/cty/function/stdlib"
 
-	"github.com/avenga/couper/config"
 	"github.com/avenga/couper/config/meta"
 	"github.com/avenga/couper/config/request"
 	"github.com/avenga/couper/errors"
@@ -100,28 +100,16 @@ func SetBody(req *http.Request, body []byte) {
 	parseForm(req)
 }
 
-func getPathAttribute(body hcl.Body) (*hcl.Attribute, error) {
-	bodyContent, _, diags := body.PartialContent(config.BackendInlineSchema)
-	if diags.HasErrors() {
-		return nil, errors.Evaluation.With(diags)
-	}
-	return bodyContent.Attributes[attrPath], nil
-}
-
-func ApplyRequestContext(httpCtx *hcl.EvalContext, body hcl.Body, req *http.Request) error {
+func ApplyRequestContext(httpCtx *hcl.EvalContext, body *hclsyntax.Body, req *http.Request) error {
 	if req == nil {
 		return nil
 	}
 
 	headerCtx := req.Header
 
-	pathAttr, err := getPathAttribute(body)
-	if err != nil {
-		return err
-	}
-
+	pathAttr := body.Attributes[attrPath]
 	if pathAttr != nil {
-		if err = evalPathAttr(req, pathAttr, httpCtx); err != nil {
+		if err := evalPathAttr(req, pathAttr, httpCtx); err != nil {
 			return err
 		}
 	}
@@ -279,7 +267,7 @@ func getFormParams(ctx *hcl.EvalContext, req *http.Request, attrs map[string]*hc
 	return nil
 }
 
-func evalPathAttr(req *http.Request, pathAttr *hcl.Attribute, httpCtx *hcl.EvalContext) error {
+func evalPathAttr(req *http.Request, pathAttr *hclsyntax.Attribute, httpCtx *hcl.EvalContext) error {
 	if pathAttr == nil {
 		return nil
 	}
@@ -377,7 +365,7 @@ func ApplyCustomLogs(httpCtx *hcl.EvalContext, bodies []hcl.Body, logger *logrus
 	return seetie.ValueToLogFields(val)
 }
 
-func ApplyResponseContext(ctx *hcl.EvalContext, body hcl.Body, beresp *http.Response) error {
+func ApplyResponseContext(ctx *hcl.EvalContext, body *hclsyntax.Body, beresp *http.Response) error {
 	if beresp == nil {
 		return nil
 	}
@@ -390,8 +378,7 @@ func ApplyResponseContext(ctx *hcl.EvalContext, body hcl.Body, beresp *http.Resp
 		return nil
 	}
 
-	bodyContent, _, _ := body.PartialContent(config.BackendInlineSchema)
-	if attr, ok := bodyContent.Attributes["set_response_status"]; ok {
+	if attr, ok := body.Attributes["set_response_status"]; ok {
 		_, err := ApplyResponseStatus(ctx, attr, beresp)
 		return err
 	}
@@ -399,7 +386,7 @@ func ApplyResponseContext(ctx *hcl.EvalContext, body hcl.Body, beresp *http.Resp
 	return nil
 }
 
-func ApplyResponseStatus(httpCtx *hcl.EvalContext, attr *hcl.Attribute, beresp *http.Response) (int, error) {
+func ApplyResponseStatus(httpCtx *hcl.EvalContext, attr *hclsyntax.Attribute, beresp *http.Response) (int, error) {
 	statusValue, err := Value(httpCtx, attr.Expr)
 	if err != nil {
 		return 0, err
@@ -487,7 +474,7 @@ func applyHeaderOps(attrs map[string]*hcl.Attribute, names []string, httpCtx *hc
 	return nil
 }
 
-func GetBody(ctx *hcl.EvalContext, content *hcl.BodyContent) (string, string, error) {
+func GetBody(ctx *hcl.EvalContext, content *hclsyntax.Body) (string, string, error) {
 	attr, ok := content.Attributes["json_body"]
 	if ok {
 		val, err := Value(ctx, attr.Expr)
diff --git a/eval/value.go b/eval/value.go
index bc308998..674a93af 100644
--- a/eval/value.go
+++ b/eval/value.go
@@ -19,25 +19,16 @@ var emptyStringVal = cty.StringVal("")
 
 // ValueFromBodyAttribute lookups the given attribute from given hcl.Body and
 // returns cty.NilVal if the attribute is not present.
-func ValueFromBodyAttribute(ctx *hcl.EvalContext, body hcl.Body, name string) (cty.Value, error) {
+func ValueFromBodyAttribute(ctx *hcl.EvalContext, body *hclsyntax.Body, name string) (cty.Value, error) {
 	if body == nil {
 		return cty.NilVal, nil
 	}
-	schema := &hcl.BodySchema{Attributes: []hcl.AttributeSchema{{Name: name}}}
-	content, _, _ := body.PartialContent(schema)
-	if content == nil || len(content.Attributes) == 0 {
-		return cty.NilVal, nil
-	}
 
-	return ValueFromAttribute(ctx, content, name)
-}
-
-// ValueFromAttribute lookups the given attribute name and returns cty.NilVal if the attribute is not present.
-func ValueFromAttribute(ctx *hcl.EvalContext, content *hcl.BodyContent, name string) (cty.Value, error) {
-	attr, ok := content.Attributes[name]
+	attr, ok := body.Attributes[name]
 	if !ok {
 		return cty.NilVal, nil
 	}
+
 	return Value(ctx, attr.Expr)
 }
 
diff --git a/handler/endpoint.go b/handler/endpoint.go
index 004847eb..675567e4 100644
--- a/handler/endpoint.go
+++ b/handler/endpoint.go
@@ -8,10 +8,10 @@ import (
 	"strconv"
 
 	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/sirupsen/logrus"
 	"go.opentelemetry.io/otel/trace"
 
-	"github.com/avenga/couper/config"
 	"github.com/avenga/couper/config/request"
 	"github.com/avenga/couper/config/runtime/server"
 	"github.com/avenga/couper/errors"
@@ -33,7 +33,7 @@ type Endpoint struct {
 type EndpointOptions struct {
 	APIName        string
 	BufferOpts     eval.BufferOption
-	Context        hcl.Body
+	Context        *hclsyntax.Body
 	ErrorTemplate  *errors.Template
 	ErrorHandler   http.Handler
 	IsErrorHandler bool
@@ -289,10 +289,8 @@ func (e *Endpoint) handleError(rw http.ResponseWriter, req *http.Request, err er
 		return true
 	}
 
-	content, _, _ := e.opts.Context.PartialContent(config.Endpoint{}.Schema(true))
-
 	// modify response status code if set
-	if attr, ok := content.Attributes["set_response_status"]; e.opts.IsErrorHandler && ctxErr == err && ok {
+	if attr, ok := e.opts.Context.Attributes["set_response_status"]; e.opts.IsErrorHandler && ctxErr == err && ok {
 		if statusCode, applyErr := eval.
 			ApplyResponseStatus(eval.ContextFromRequest(req).HCLContextSync(), attr, nil); statusCode > 0 {
 			if serr, k := serveErr.(*errors.Error); k {
diff --git a/handler/producer/proxy.go b/handler/producer/proxy.go
index 1d0b066a..6719aceb 100644
--- a/handler/producer/proxy.go
+++ b/handler/producer/proxy.go
@@ -7,7 +7,7 @@ import (
 	"runtime/debug"
 	"sync"
 
-	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"go.opentelemetry.io/otel/trace"
 
 	"github.com/avenga/couper/config/request"
@@ -16,7 +16,7 @@ import (
 )
 
 type Proxy struct {
-	Content          hcl.Body
+	Content          *hclsyntax.Body
 	Name             string // label
 	PreviousSequence string
 	RoundTrip        http.RoundTripper
diff --git a/handler/producer/request.go b/handler/producer/request.go
index 98c533b5..f54bac85 100644
--- a/handler/producer/request.go
+++ b/handler/producer/request.go
@@ -8,11 +8,10 @@ import (
 	"strings"
 	"sync"
 
-	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 	semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
 	"go.opentelemetry.io/otel/trace"
 
-	"github.com/avenga/couper/config"
 	"github.com/avenga/couper/config/request"
 	"github.com/avenga/couper/eval"
 	"github.com/avenga/couper/internal/seetie"
@@ -22,7 +21,7 @@ import (
 // Request represents the producer <Request> object.
 type Request struct {
 	Backend          http.RoundTripper
-	Context          hcl.Body
+	Context          *hclsyntax.Body
 	Name             string // label
 	PreviousSequence string
 }
@@ -60,13 +59,7 @@ func (r Requests) Produce(req *http.Request, results chan<- *Result) {
 			outCtx = context.WithValue(outCtx, request.EndpointSequenceDependsOn, or.PreviousSequence)
 		}
 
-		bodyContent, _, diags := or.Context.PartialContent(config.Request{Remain: or.Context}.Schema(true))
-		if diags.HasErrors() {
-			results <- &Result{Err: diags}
-			continue
-		}
-
-		methodVal, err := eval.ValueFromAttribute(hclCtx, bodyContent, "method")
+		methodVal, err := eval.ValueFromBodyAttribute(hclCtx, or.Context, "method")
 		if err != nil {
 			results <- &Result{Err: err}
 			continue
@@ -82,7 +75,7 @@ func (r Requests) Produce(req *http.Request, results chan<- *Result) {
 			continue
 		}
 
-		body, defaultContentType, err := eval.GetBody(hclCtx, bodyContent)
+		body, defaultContentType, err := eval.GetBody(hclCtx, or.Context)
 		if err != nil {
 			results <- &Result{Err: err}
 			continue
@@ -102,7 +95,7 @@ func (r Requests) Produce(req *http.Request, results chan<- *Result) {
 			continue
 		}
 
-		expStatusVal, err := eval.ValueFromAttribute(hclCtx, bodyContent, "expected_status")
+		expStatusVal, err := eval.ValueFromBodyAttribute(hclCtx, or.Context, "expected_status")
 		if err != nil {
 			results <- &Result{Err: err}
 			continue
diff --git a/handler/producer/response.go b/handler/producer/response.go
index 3819e008..a0e9883c 100644
--- a/handler/producer/response.go
+++ b/handler/producer/response.go
@@ -5,9 +5,8 @@ import (
 	"net/http"
 	"strings"
 
-	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
-	"github.com/avenga/couper/config"
 	"github.com/avenga/couper/errors"
 	"github.com/avenga/couper/eval"
 	"github.com/avenga/couper/internal/seetie"
@@ -15,10 +14,10 @@ import (
 
 // Response represents the producer <Response> object.
 type Response struct {
-	Context hcl.Body
+	Context *hclsyntax.Body
 }
 
-func NewResponse(req *http.Request, resp hcl.Body, statusCode int) (*http.Response, error) {
+func NewResponse(req *http.Request, resp *hclsyntax.Body, statusCode int) (*http.Response, error) {
 	clientres := &http.Response{
 		Header:     make(http.Header),
 		Proto:      req.Proto,
@@ -29,12 +28,7 @@ func NewResponse(req *http.Request, resp hcl.Body, statusCode int) (*http.Respon
 
 	hclCtx := eval.ContextFromRequest(req).HCLContextSync()
 
-	content, _, diags := resp.PartialContent(config.ResponseInlineSchema)
-	if diags.HasErrors() {
-		return nil, errors.Evaluation.With(diags)
-	}
-
-	if attr, ok := content.Attributes["status"]; ok {
+	if attr, ok := resp.Attributes["status"]; ok {
 		val, err := eval.Value(hclCtx, attr.Expr)
 		if err != nil {
 			return nil, err
@@ -45,7 +39,7 @@ func NewResponse(req *http.Request, resp hcl.Body, statusCode int) (*http.Respon
 	clientres.StatusCode = statusCode
 	clientres.Status = http.StatusText(clientres.StatusCode)
 
-	respBody, ct, bodyErr := eval.GetBody(hclCtx, content)
+	respBody, ct, bodyErr := eval.GetBody(hclCtx, resp)
 	if bodyErr != nil {
 		return nil, errors.Evaluation.With(bodyErr)
 	}
@@ -54,7 +48,7 @@ func NewResponse(req *http.Request, resp hcl.Body, statusCode int) (*http.Respon
 		clientres.Header.Set("Content-Type", ct)
 	}
 
-	if attr, ok := content.Attributes["headers"]; ok {
+	if attr, ok := resp.Attributes["headers"]; ok {
 		val, err := eval.Value(hclCtx, attr.Expr)
 		if err != nil {
 			return nil, err
diff --git a/handler/producer/url.go b/handler/producer/url.go
index ed657499..abdcf17f 100644
--- a/handler/producer/url.go
+++ b/handler/producer/url.go
@@ -7,6 +7,7 @@ import (
 	"strings"
 
 	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/zclconf/go-cty/cty"
 
 	"github.com/avenga/couper/config/request"
@@ -14,7 +15,7 @@ import (
 	"github.com/avenga/couper/utils"
 )
 
-func NewURLFromAttribute(hclCtx *hcl.EvalContext, content hcl.Body, attrName string, req *http.Request) (*url.URL, error) {
+func NewURLFromAttribute(hclCtx *hcl.EvalContext, content *hclsyntax.Body, attrName string, req *http.Request) (*url.URL, error) {
 	urlVal, err := eval.ValueFromBodyAttribute(hclCtx, content, attrName)
 	if err != nil {
 		return nil, err
diff --git a/handler/proxy.go b/handler/proxy.go
index efcaebdb..ab8d5cae 100644
--- a/handler/proxy.go
+++ b/handler/proxy.go
@@ -10,10 +10,10 @@ import (
 	"sync"
 	"time"
 
-	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/sirupsen/logrus"
 
-	"github.com/avenga/couper/config"
+	hclbody "github.com/avenga/couper/config/body"
 	"github.com/avenga/couper/config/request"
 	"github.com/avenga/couper/errors"
 	"github.com/avenga/couper/eval"
@@ -31,11 +31,11 @@ var headerBlacklist = []string{"Authorization", "Cookie"}
 // and have control over the roundtrip configuration.
 type Proxy struct {
 	backend http.RoundTripper
-	context hcl.Body
+	context *hclsyntax.Body
 	logger  *logrus.Entry
 }
 
-func NewProxy(backend http.RoundTripper, ctx hcl.Body, logger *logrus.Entry) *Proxy {
+func NewProxy(backend http.RoundTripper, ctx *hclsyntax.Body, logger *logrus.Entry) *Proxy {
 	proxy := &Proxy{
 		backend: backend,
 		context: ctx,
@@ -154,20 +154,12 @@ func (p *Proxy) applyWebsocketsRequest(req *http.Request) error {
 		return nil
 	}
 
-	wsBody, err := p.getWebsocketsBody()
-	if err != nil {
-		return err
-	}
-
-	bodyContent, _, diags := wsBody.PartialContent(config.WebsocketsInlineSchema)
-	if diags.HasErrors() {
-		return diags
-	}
+	wsBody := p.getWebsocketsBody()
 	if err := eval.ApplyRequestContext(hclCtx, wsBody, req); err != nil {
 		return err
 	}
 
-	attr, ok := bodyContent.Attributes["timeout"]
+	attr, ok := wsBody.Attributes["timeout"]
 	if !ok {
 		return nil
 	}
@@ -201,11 +193,7 @@ func (p *Proxy) registerWebsocketsResponse(req *http.Request) error {
 		return nil
 	}
 
-	wsBody, err := p.getWebsocketsBody()
-	if err != nil {
-		return err
-	}
-
+	wsBody := p.getWebsocketsBody()
 	evalCtx := eval.ContextFromRequest(req)
 
 	if rw, ok := req.Context().Value(request.ResponseWriter).(*writer.Response); ok {
@@ -215,18 +203,13 @@ func (p *Proxy) registerWebsocketsResponse(req *http.Request) error {
 	return nil
 }
 
-func (p *Proxy) getWebsocketsBody() (hcl.Body, error) {
-	bodyContent, _, diags := p.context.PartialContent(config.Proxy{Remain: p.context}.Schema(true))
-	if diags.HasErrors() {
-		return nil, diags
-	}
-
-	wss := bodyContent.Blocks.OfType("websockets")
+func (p *Proxy) getWebsocketsBody() *hclsyntax.Body {
+	wss := hclbody.BlocksOfType(p.context, "websockets")
 	if len(wss) != 1 {
-		return nil, nil
+		return nil
 	}
 
-	return wss[0].Body, nil
+	return wss[0].Body
 }
 
 func (p *Proxy) handleUpgradeResponse(req *http.Request, res *http.Response) error {
diff --git a/handler/transport/backend.go b/handler/transport/backend.go
index 7070435c..b3517e9a 100644
--- a/handler/transport/backend.go
+++ b/handler/transport/backend.go
@@ -13,6 +13,7 @@ import (
 	"time"
 
 	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/sirupsen/logrus"
 	"github.com/zclconf/go-cty/cty"
 	"go.opentelemetry.io/otel/attribute"
@@ -44,7 +45,7 @@ var (
 )
 
 type Backend struct {
-	context             hcl.Body
+	context             *hclsyntax.Body
 	healthInfo          *HealthInfo
 	healthyMu           sync.RWMutex
 	logEntry            *logrus.Entry
@@ -59,7 +60,7 @@ type Backend struct {
 }
 
 // NewBackend creates a new <*Backend> object by the given <*Config>.
-func NewBackend(ctx hcl.Body, tc *Config, opts *BackendOptions, log *logrus.Entry) http.RoundTripper {
+func NewBackend(ctx *hclsyntax.Body, tc *Config, opts *BackendOptions, log *logrus.Entry) http.RoundTripper {
 	var (
 		healthCheck       *config.HealthCheck
 		openAPI           *validation.OpenAPI
@@ -112,11 +113,11 @@ func (b *Backend) initOnce(conf *Config) {
 
 // RoundTrip implements the <http.RoundTripper> interface.
 func (b *Backend) RoundTrip(req *http.Request) (*http.Response, error) {
-	ctxBody, _ := req.Context().Value(request.BackendParams).(hcl.Body)
+	ctxBody, _ := req.Context().Value(request.BackendParams).(*hclsyntax.Body)
 	if ctxBody == nil {
 		ctxBody = b.context
 	} else {
-		ctxBody = hclbody.MergeBodies(b.context, ctxBody)
+		ctxBody = hclbody.MergeBodies(ctxBody, b.context, false)
 	}
 
 	// originalReq for token-request retry purposes
@@ -393,7 +394,7 @@ func (b *Backend) withRetryTokenRequest(req *http.Request, res *http.Response) (
 	return retry, nil
 }
 
-func (b *Backend) withPathPrefix(req *http.Request, evalCtx *hcl.EvalContext, hclContext hcl.Body) error {
+func (b *Backend) withPathPrefix(req *http.Request, evalCtx *hcl.EvalContext, hclContext *hclsyntax.Body) error {
 	if pathPrefix := b.getAttribute(evalCtx, "path_prefix", hclContext); pathPrefix != "" {
 		// TODO: Check for a valid absolute path
 		if i := strings.Index(pathPrefix, "#"); i >= 0 {
@@ -408,14 +409,14 @@ func (b *Backend) withPathPrefix(req *http.Request, evalCtx *hcl.EvalContext, hc
 	return nil
 }
 
-func (b *Backend) withBasicAuth(req *http.Request, evalCtx *hcl.EvalContext, hclContext hcl.Body) {
+func (b *Backend) withBasicAuth(req *http.Request, evalCtx *hcl.EvalContext, hclContext *hclsyntax.Body) {
 	if creds := b.getAttribute(evalCtx, "basic_auth", hclContext); creds != "" {
 		auth := base64.StdEncoding.EncodeToString([]byte(creds))
 		req.Header.Set("Authorization", "Basic "+auth)
 	}
 }
 
-func (b *Backend) getAttribute(evalContext *hcl.EvalContext, name string, hclContext hcl.Body) string {
+func (b *Backend) getAttribute(evalContext *hcl.EvalContext, name string, hclContext *hclsyntax.Body) string {
 	attrVal, err := eval.ValueFromBodyAttribute(evalContext, hclContext, name)
 	if err != nil {
 		b.upstreamLog.LogEntry().WithError(errors.Evaluation.Label(b.name).With(err))
@@ -500,14 +501,9 @@ func (b *Backend) withTimeout(req *http.Request, conf *Config) <-chan error {
 	return errCh
 }
 
-func (b *Backend) evalTransport(httpCtx *hcl.EvalContext, params hcl.Body, req *http.Request) (*Config, error) {
+func (b *Backend) evalTransport(httpCtx *hcl.EvalContext, params *hclsyntax.Body, req *http.Request) (*Config, error) {
 	log := b.upstreamLog.LogEntry()
 
-	bodyContent, _, diags := params.PartialContent(config.BackendInlineSchema)
-	if diags.HasErrors() {
-		return nil, errors.Evaluation.Label(b.name).With(diags)
-	}
-
 	var origin, hostname, proxyURL string
 	var connectTimeout, ttfbTimeout, timeout string
 	type pair struct {
@@ -523,7 +519,7 @@ func (b *Backend) evalTransport(httpCtx *hcl.EvalContext, params hcl.Body, req *
 		{"ttfb_timeout", &ttfbTimeout},
 		{"timeout", &timeout},
 	} {
-		if v, err := eval.ValueFromAttribute(httpCtx, bodyContent, p.attrName); err != nil {
+		if v, err := eval.ValueFromBodyAttribute(httpCtx, params, p.attrName); err != nil {
 			log.WithError(errors.Evaluation.Label(b.name).With(err)).Error()
 		} else if v != cty.NilVal {
 			*p.target = seetie.ValueToString(v)
@@ -554,12 +550,8 @@ func (b *Backend) evalTransport(httpCtx *hcl.EvalContext, params hcl.Body, req *
 		WithTimings(connectTimeout, ttfbTimeout, timeout, log), nil
 }
 
-func (b *Backend) isUnhealthy(ctx *hcl.EvalContext, params hcl.Body) error {
-	paramsContent, _, diags := params.PartialContent(config.BackendInlineSchema)
-	if diags.HasErrors() {
-		return diags
-	}
-	val, err := eval.ValueFromAttribute(ctx, paramsContent, "use_when_unhealthy")
+func (b *Backend) isUnhealthy(ctx *hcl.EvalContext, params *hclsyntax.Body) error {
+	val, err := eval.ValueFromBodyAttribute(ctx, params, "use_when_unhealthy")
 	if err != nil {
 		return err
 	}
diff --git a/handler/transport/token_request.go b/handler/transport/token_request.go
index 71280431..4ff9d2eb 100644
--- a/handler/transport/token_request.go
+++ b/handler/transport/token_request.go
@@ -90,17 +90,12 @@ func (t *TokenRequest) requestToken(req *http.Request) (string, int64, error) {
 		return "", 0, fmt.Errorf("token request failed") // don't propagate token request roundtrip error
 	}
 
-	trConf := &config.TokenRequest{Remain: t.config.Remain}
-	bodyContent, _, diags := t.config.Remain.PartialContent(trConf.Schema(true))
-	if diags.HasErrors() {
-		return "", 0, diags
-	}
-
 	// obtain synced and already read beresp value; map to context variables
 	hclCtx := eval.ContextFromRequest(req).HCLContextSync()
 	eval.MapTokenResponse(hclCtx, t.config.Name)
 
-	tokenVal, err := eval.ValueFromAttribute(hclCtx, bodyContent, "token")
+	tokenRequestBody := t.config.HCLBody()
+	tokenVal, err := eval.ValueFromBodyAttribute(hclCtx, tokenRequestBody, "token")
 	if err != nil {
 		return "", 0, err
 	}
@@ -111,7 +106,7 @@ func (t *TokenRequest) requestToken(req *http.Request) (string, int64, error) {
 		return "", 0, fmt.Errorf("token expression must evaluate to a string")
 	}
 
-	ttlVal, err := eval.ValueFromAttribute(hclCtx, bodyContent, "ttl")
+	ttlVal, err := eval.ValueFromBodyAttribute(hclCtx, tokenRequestBody, "ttl")
 	if err != nil {
 		return "", 0, err
 	}
diff --git a/oauth2/abstract_auth_client.go b/oauth2/abstract_auth_client.go
index 19236910..d4f446a1 100644
--- a/oauth2/abstract_auth_client.go
+++ b/oauth2/abstract_auth_client.go
@@ -44,7 +44,7 @@ func (a AbstractAuthCodeClient) ExchangeCodeAndGetTokenResponse(req *http.Reques
 	}
 
 	ctx := eval.ContextFromRequest(req).HCLContext()
-	verifierVal, err := eval.ValueFromBodyAttribute(ctx, a.clientConfig.HCLBody(), "verifier_value")
+	verifierVal, err := eval.ValueFromBodyAttribute(ctx, a.acClientConf.HCLBody(), "verifier_value")
 	if err != nil {
 		return nil, errors.Oauth2.With(err)
 	}
diff --git a/oauth2/oidc/config.go b/oauth2/oidc/config.go
index 2cc60728..a2fcdd18 100644
--- a/oauth2/oidc/config.go
+++ b/oauth2/oidc/config.go
@@ -185,8 +185,8 @@ func (c *Config) Unmarshal(rawJSON []byte) (interface{}, error) {
 		}
 	}
 
-	jwksBackend := backend.NewContext(hclbody.
-		New(hclbody.NewContentWithAttrName("_backend_url", jsonData.JwksURI)),
+	jwksBackend := backend.NewContext(
+		hclbody.NewHCLSyntaxBodyWithStringAttr("_backend_url", jsonData.JwksURI),
 		c.backends["jwks_uri_backend"],
 	)
 


Test patch:
diff --git a/accesscontrol/jwk/jwks_test.go b/accesscontrol/jwk/jwks_test.go
index 580051dc..36a63e31 100644
--- a/accesscontrol/jwk/jwks_test.go
+++ b/accesscontrol/jwk/jwks_test.go
@@ -25,7 +25,7 @@ func Test_JWKS(t *testing.T) {
 
 	log, _ := test.NewLogger()
 
-	backend := transport.NewBackend(body.New(body.NewContentWithAttrName("origin", origin.Addr())),
+	backend := transport.NewBackend(body.NewHCLSyntaxBodyWithStringAttr("origin", origin.Addr()),
 		&transport.Config{}, nil, log.WithContext(context.Background()))
 
 	tests := []struct {
diff --git a/config/body/body_test.go b/config/body/body_test.go
index 7f39f0b0..459eebb5 100644
--- a/config/body/body_test.go
+++ b/config/body/body_test.go
@@ -1,63 +1,127 @@
 package body_test
 
 import (
-	"reflect"
 	"testing"
 
-	"github.com/avenga/couper/config/body"
-	"github.com/hashicorp/hcl/v2"
-	"github.com/hashicorp/hcl/v2/hcltest"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/zclconf/go-cty/cty"
+
+	"github.com/avenga/couper/config/body"
+	"github.com/avenga/couper/eval"
 )
 
-func TestBody_Body(t *testing.T) {
-	itemRange := hcl.Range{
-		Filename: "test.hcl",
-		Start:    hcl.Pos{Line: 1, Column: 2, Byte: 3},
-		End:      hcl.Pos{Line: 4, Column: 5, Byte: 6},
-	}
-	attrs := hcl.Attributes{
-		"name": &hcl.Attribute{
-			Name: "test",
-			Expr: hcltest.MockExprLiteral(cty.StringVal("value")),
+func TestBody_MergeBodies(t *testing.T) {
+	tests := []struct {
+		name            string
+		src             *hclsyntax.Body
+		replace         bool
+		expAttrs        map[string]string
+		expBlocksTotal  int
+		expBlocksOfType map[string]int
+	}{
+		{
+			"merge, replace",
+			&hclsyntax.Body{
+				Attributes: hclsyntax.Attributes{
+					"a": &hclsyntax.Attribute{Name: "a", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("a2")}},
+					"c": &hclsyntax.Attribute{Name: "c", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("c")}},
+				},
+				Blocks: hclsyntax.Blocks{
+					&hclsyntax.Block{Type: "a", Labels: []string{}, Body: &hclsyntax.Body{}},
+					&hclsyntax.Block{Type: "b", Labels: []string{"label"}, Body: &hclsyntax.Body{}},
+					&hclsyntax.Block{Type: "c", Labels: []string{"label"}, Body: &hclsyntax.Body{}},
+				},
+			},
+			true,
+			map[string]string{"a": "a2", "b": "b", "c": "c"},
+			6,
+			map[string]int{"a": 2, "b": 2, "c": 2},
+		},
+		{
+			"merge, no replace",
+			&hclsyntax.Body{
+				Attributes: hclsyntax.Attributes{
+					"a": &hclsyntax.Attribute{Name: "a", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("a2")}},
+					"c": &hclsyntax.Attribute{Name: "c", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("c")}},
+				},
+				Blocks: hclsyntax.Blocks{
+					&hclsyntax.Block{Type: "a", Labels: []string{}, Body: &hclsyntax.Body{}},
+					&hclsyntax.Block{Type: "b", Labels: []string{"label"}, Body: &hclsyntax.Body{}},
+					&hclsyntax.Block{Type: "c", Labels: []string{"label"}, Body: &hclsyntax.Body{}},
+				},
+			},
+			false,
+			map[string]string{"a": "a1", "b": "b", "c": "c"},
+			6,
+			map[string]int{"a": 2, "b": 2, "c": 2},
+		},
+		{
+			"'merge' with self, replace",
+			nil,
+			true,
+			map[string]string{"a": "a1", "b": "b"},
+			3,
+			map[string]int{"a": 1, "b": 1, "c": 1},
+		},
+		{
+			"'merge' with self, no replace",
+			nil,
+			false,
+			map[string]string{"a": "a1", "b": "b"},
+			3,
+			map[string]int{"a": 1, "b": 1, "c": 1},
 		},
 	}
 
-	content := &hcl.BodyContent{
-		Attributes:       attrs,
-		MissingItemRange: itemRange,
-	}
-	body := body.New(content)
-
-	mir := body.MissingItemRange()
-	if !reflect.DeepEqual(mir, itemRange) {
-		t.Errorf("want\n%#v\ngot\n%#v", itemRange, mir)
-	}
-
-	c, diags := body.Content(nil)
-	if diags != nil {
-		t.Errorf("Unexpected diags: %#v", diags)
-	}
-	if !reflect.DeepEqual(c, content) {
-		t.Errorf("want\n%#v\ngot\n%#v", content, c)
-	}
-
-	p, b, diags := body.PartialContent(nil)
-	if diags != nil {
-		t.Errorf("Unexpected diags: %#v", diags)
-	}
-	if !reflect.DeepEqual(p, content) {
-		t.Errorf("want\n%#v\ngot\n%#v", content, p)
-	}
-	if !reflect.DeepEqual(b, body) {
-		t.Errorf("want\n%#v\ngot\n%#v", body, b)
-	}
+	hclcontext := eval.NewDefaultContext().HCLContext()
 
-	a, diags := body.JustAttributes()
-	if diags != nil {
-		t.Errorf("Unexpected diags: %#v", diags)
-	}
-	if !reflect.DeepEqual(a, attrs) {
-		t.Errorf("want\n%#v\ngot\n%#v", attrs, a)
+	for _, tt := range tests {
+		t.Run(tt.name, func(subT *testing.T) {
+			dest := &hclsyntax.Body{
+				Attributes: hclsyntax.Attributes{
+					"a": &hclsyntax.Attribute{Name: "a", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("a1")}},
+					"b": &hclsyntax.Attribute{Name: "b", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("b")}},
+				},
+				Blocks: hclsyntax.Blocks{
+					&hclsyntax.Block{Type: "a", Labels: []string{}, Body: &hclsyntax.Body{}},
+					&hclsyntax.Block{Type: "b", Labels: []string{"label"}, Body: &hclsyntax.Body{}},
+					&hclsyntax.Block{Type: "c", Labels: []string{""}, Body: &hclsyntax.Body{}},
+				},
+			}
+			src := tt.src
+			if tt.src == nil {
+				src = dest
+			}
+			merged := body.MergeBodies(dest, src, tt.replace)
+			if len(merged.Attributes) != len(tt.expAttrs) {
+				subT.Fatalf("expected %d attributes, was %d", len(tt.expAttrs), len(merged.Attributes))
+			}
+			for k, expV := range tt.expAttrs {
+				attr, set := merged.Attributes[k]
+				if !set {
+					subT.Errorf("expected attribute %q set", k)
+				}
+				val, diags := attr.Expr.Value(hclcontext)
+				if diags.HasErrors() {
+					subT.Error(diags)
+				}
+				sVal := val.AsString()
+				if val.AsString() != expV {
+					subT.Errorf("attribute value for %q:\nwant: %q\ngot:  %q", k, expV, sVal)
+				}
+			}
+			if len(merged.Blocks) != tt.expBlocksTotal {
+				subT.Fatalf("expected %d blocks, was %d", tt.expBlocksTotal, len(merged.Blocks))
+			}
+			if len(body.BlocksOfType(merged, "a")) != tt.expBlocksOfType["a"] {
+				subT.Errorf("expected %d blocks of type a", tt.expBlocksOfType["a"])
+			}
+			if len(body.BlocksOfType(merged, "b")) != tt.expBlocksOfType["b"] {
+				subT.Errorf("expected %d blocks of type b", tt.expBlocksOfType["b"])
+			}
+			if len(body.BlocksOfType(merged, "c")) != tt.expBlocksOfType["c"] {
+				subT.Errorf("expected %d blocks of type c", tt.expBlocksOfType["c"])
+			}
+		})
 	}
 }
diff --git a/config/body/merged_test.go b/config/body/merged_test.go
deleted file mode 100644
index e2d77d4d..00000000
--- a/config/body/merged_test.go
+++ /dev/null
@@ -1,203 +0,0 @@
-package body_test
-
-import (
-	"testing"
-
-	"github.com/hashicorp/hcl/v2"
-	"github.com/hashicorp/hcl/v2/gohcl"
-	"github.com/hashicorp/hcl/v2/hclsyntax"
-	"github.com/hashicorp/hcl/v2/hclwrite"
-	"github.com/zclconf/go-cty/cty"
-
-	"github.com/avenga/couper/config"
-	"github.com/avenga/couper/config/body"
-	"github.com/avenga/couper/eval"
-)
-
-func TestMergeBodies(t *testing.T) {
-	type expectedBody struct {
-		OAuth2       *config.OAuth2ReqAuth `hcl:"oauth2,block"`
-		Request      *config.Request       `hcl:"request,block"`
-		TokenRequest *config.Request       `hcl:"token_request,block"`
-	}
-
-	type container struct {
-		Block []*expectedBody `hcl:"block,block"`
-	}
-
-	bodies := &container{[]*expectedBody{
-		{OAuth2: &config.OAuth2ReqAuth{
-			BackendName: "test",
-			GrantType:   "override_me",
-		}},
-		{OAuth2: &config.OAuth2ReqAuth{
-			GrantType: "no_creds",
-		}},
-	}}
-
-	expectedAttributes := map[string]*hcl.Attribute{
-		"backend":        {Name: "backend", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("test")}},
-		"grant_type":     {Name: "grant_type", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("no_creds")}},
-		"token_endpoint": {Name: "token_endpoint", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("http://that")}},
-		"url":            {Name: "url", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("https://that")}},
-		"attr1":          {Name: "attr1", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("https://bar")}},
-		"attr2":          {Name: "attr2", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("https://the-force")}},
-	}
-
-	var hclBodies []hcl.Body
-
-	// could be done with hclmock package, but for example purposes encode and read
-	target := hclwrite.NewEmptyFile()
-	gohcl.EncodeIntoBody(bodies, target.Body())
-	// write config reference
-	//buf := &bytes.Buffer{}
-	//_, _ = target.WriteTo(buf)
-
-	configBytes := []byte(`
-block {
-
-  oauth2 {
-    backend        = "test"
-    grant_type     = "no_creds"
-    token_endpoint = "http://this"
-  }
-
-  request "label" {
-    url = "https://this"
-  }
-
-  request "default" {
-    attr1 = "https://foo"
-  }
-
-  token_request "default" {
-    attr2 = "https://may-"
-  }
-}
-
-block {
-
-  oauth2 {
-    token_endpoint = "http://that"
-  }
-
-  request "label" {
-    url = "https://that"
-  }
-
-  request "default" {
-    attr1 = "https://bar"
-  }
-
-  token_request "default" {
-    attr2 = "https://the-force"
-  }
-}`)
-
-	conf, diags := hclsyntax.ParseConfig(configBytes, "testcase.hcl", hcl.InitialPos)
-	if diags.HasErrors() {
-		t.Error(diags)
-	}
-	defer func() {
-		if t.Failed() {
-			println(string(configBytes))
-		}
-	}()
-
-	schema, _ := gohcl.ImpliedBodySchema(&container{})
-	content, _, diags := conf.Body.PartialContent(schema)
-	if diags.HasErrors() {
-		t.Error(diags)
-	}
-	for _, inner := range content.Blocks.OfType("block") {
-		hclBodies = append(hclBodies, inner.Body)
-	}
-
-	schema, _ = gohcl.ImpliedBodySchema(bodies.Block[0])
-	content, _, diags = body.MergeBodies(hclBodies[:]...).PartialContent(schema)
-	if diags.HasErrors() {
-		t.Error(diags)
-	}
-
-	// after merge, we expect a single block with a body of type mergedBodies
-	if len(content.Blocks.OfType("oauth2")) != 1 {
-		t.Error("expected just one merged oauth2 block")
-	}
-	oauthBlockContent := content.Blocks.OfType("oauth2")[0]
-	resultAttributes, diags := oauthBlockContent.Body.JustAttributes()
-	if diags.HasErrors() {
-		t.Error(diags)
-	}
-
-	// same applies to labeled ones, after merge, we expect a single block with a body of type mergedBodies
-	if len(content.Blocks.OfType("token_request")) != 1 {
-		t.Error("expected one merged token_request block")
-	}
-	if len(content.Blocks.OfType("request")) != 2 {
-		t.Error("expected two merged request blocks")
-	}
-
-	for _, requestBlockContent := range append(content.Blocks.OfType("request"),
-		content.Blocks.OfType("token_request")...) {
-		requestBlockAttrs, diags := requestBlockContent.Body.JustAttributes()
-		if diags.HasErrors() {
-			t.Error(diags)
-		}
-		// caution; block attrs must differ (unique attribute names)
-		for k, v := range requestBlockAttrs {
-			resultAttributes[k] = v
-		}
-	}
-
-	hclcontext := eval.NewDefaultContext().HCLContext()
-
-	for k, attr := range expectedAttributes {
-		a, exist := resultAttributes[k]
-		if !exist {
-			t.Errorf("missing attribute %q", k)
-			continue
-		}
-
-		expVal, diags := attr.Expr.Value(hclcontext)
-		if diags.HasErrors() {
-			t.Error(diags)
-		}
-
-		val, diags := a.Expr.Value(hclcontext)
-		if diags.HasErrors() {
-			t.Error(diags)
-		}
-
-		if expVal.AsString() != val.AsString() {
-			t.Errorf("Want: %q, got %q", expVal.AsString(), val.AsString())
-		}
-	}
-}
-
-func Test_stringSliceEquals(t *testing.T) {
-	type args struct {
-		left  []string
-		right []string
-	}
-	tests := []struct {
-		name string
-		args args
-		want bool
-	}{
-		{"join glue", args{[]string{"a", "ab"}, []string{"aa", "b"}}, false},
-		{"join", args{[]string{"aa", "bb"}, []string{"bb", "aa"}}, true},
-		{"join", args{[]string{"aa", "bb"}, nil}, false},
-		{"join", args{nil, []string{"aa", "bb"}}, false},
-		{"join", args{nil, nil}, true},
-		{"join", args{[]string{""}, []string{"", ""}}, false},
-		{"join", args{[]string{"", ""}, []string{"", ""}}, true},
-		{"join", args{[]string{""}, []string{""}}, true},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if got := body.StringSliceEquals(tt.args.left, tt.args.right); got != tt.want {
-				t.Errorf("StringSliceEquals() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
diff --git a/config/configload/error_handler_test.go b/config/configload/error_handler_test.go
index 6d340834..962016eb 100644
--- a/config/configload/error_handler_test.go
+++ b/config/configload/error_handler_test.go
@@ -6,10 +6,11 @@ import (
 	"testing"
 
 	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 )
 
 func TestErrorHandler_newKindsFromLabels(t *testing.T) {
-	b := &hcl.Block{
+	b := &hclsyntax.Block{
 		Labels: []string{""},
 		LabelRanges: []hcl.Range{
 			{
diff --git a/config/configload/load_test.go b/config/configload/load_test.go
index ae5d1f31..3f929645 100644
--- a/config/configload/load_test.go
+++ b/config/configload/load_test.go
@@ -462,3 +462,83 @@ func TestEnvironmentBlocksWithoutEnvironment(t *testing.T) {
 		})
 	}
 }
+
+func TestConfigErrors(t *testing.T) {
+	tests := []struct {
+		name  string
+		hcl   string
+		error string
+	}{
+		{
+			"websockets attribute and block",
+			`proxy {
+		      backend = "foo"
+		      websockets = true
+		      websockets {}
+		    }`,
+			"couper.hcl:6,9-26: either websockets attribute or block is allowed; ",
+		},
+		{
+			"unlabeled proxy and request",
+			`proxy {
+		      backend {}
+		    }
+		    request {
+		      url = "http://foo/bar"
+		      backend {}
+		    }`,
+			`couper.hcl:3,18-18: proxy and request names (either default or explicitly set via label) must be unique: "default"; `,
+		},
+		{
+			"unlabeled proxy and default labeled request",
+			`proxy {
+		      backend {}
+		    }
+		    request "default" {
+		      url = "http://foo/bar"
+		      backend {}
+		    }`,
+			`couper.hcl:3,18-18: proxy and request names (either default or explicitly set via label) must be unique: "default"; `,
+		},
+		{
+			"default labeled proxy and unlabeled request",
+			`proxy "default" {
+		      backend {}
+		    }
+		    request {
+		      url = "http://foo/bar"
+		      backend {}
+		    }`,
+			`couper.hcl:3,18-18: proxy and request names (either default or explicitly set via label) must be unique: "default"; `,
+		},
+		{
+			"labeled proxy and request",
+			`proxy "foo" {
+		      backend {}
+		    }
+		    request "foo" {
+		      url = "http://foo/bar"
+		      backend {}
+		    }`,
+			`couper.hcl:3,18-18: proxy and request names (either default or explicitly set via label) must be unique: "foo"; `,
+		},
+	}
+
+	template := `
+		server {
+		  endpoint "/" {
+		    %%
+		  }
+		}`
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(subT *testing.T) {
+			_, err := configload.LoadBytes([]byte(strings.Replace(template, "%%", tt.hcl, -1)), "couper.hcl")
+
+			errorMsg := err.Error()
+			if tt.error != errorMsg {
+				subT.Errorf("%q: Unexpected configuration error:\n\tWant: %q\n\tGot:  %q", tt.name, tt.error, errorMsg)
+			}
+		})
+	}
+}
diff --git a/config/configload/validate_test.go b/config/configload/validate_test.go
index 2d2979f5..ecbebde2 100644
--- a/config/configload/validate_test.go
+++ b/config/configload/validate_test.go
@@ -5,7 +5,7 @@ import (
 	"fmt"
 	"testing"
 
-	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/cache"
 	"github.com/avenga/couper/config/runtime"
@@ -15,35 +15,35 @@ import (
 
 func Test_VerifyBodyAttributes(t *testing.T) {
 	type testCase struct {
-		name    string
-		content *hcl.BodyContent
-		expErr  bool
+		name   string
+		body   *hclsyntax.Body
+		expErr bool
 	}
 
 	for _, tc := range []testCase{
-		{"without any body", &hcl.BodyContent{}, false},
-		{"body", &hcl.BodyContent{Attributes: map[string]*hcl.Attribute{"body": {Name: "body"}}}, false},
-		{"json_body", &hcl.BodyContent{Attributes: map[string]*hcl.Attribute{"json_body": {Name: "json_body"}}}, false},
-		{"form_body", &hcl.BodyContent{Attributes: map[string]*hcl.Attribute{"form_body": {Name: "form_body"}}}, false},
-		{"body/form_body", &hcl.BodyContent{Attributes: map[string]*hcl.Attribute{
+		{"without any body attributes", &hclsyntax.Body{}, false},
+		{"body", &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{"body": {Name: "body"}}}, false},
+		{"json_body", &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{"json_body": {Name: "json_body"}}}, false},
+		{"form_body", &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{"form_body": {Name: "form_body"}}}, false},
+		{"body/form_body", &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{
 			"body":      {Name: "body"},
 			"form_body": {Name: "form_body"},
 		}}, true},
-		{"body/json_body", &hcl.BodyContent{Attributes: map[string]*hcl.Attribute{
+		{"body/json_body", &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{
 			"body":      {Name: "body"},
 			"json_body": {Name: "json_body"},
 		}}, true},
-		{"form_body/json_body", &hcl.BodyContent{Attributes: map[string]*hcl.Attribute{
+		{"form_body/json_body", &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{
 			"form_body": {Name: "form_body"},
 			"json_body": {Name: "json_body"},
 		}}, true},
-		{"body/json_body/form_body", &hcl.BodyContent{Attributes: map[string]*hcl.Attribute{
+		{"body/json_body/form_body", &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{
 			"body":      {Name: "body"},
 			"json_body": {Name: "json_body"},
 			"form_body": {Name: "form_body"},
 		}}, true},
 	} {
-		if err := verifyBodyAttributes(request, tc.content); !tc.expErr && err != nil {
+		if err := verifyBodyAttributes(request, tc.body); !tc.expErr && err != nil {
 			t.Errorf("Want no error, got: %v", err)
 		}
 	}
diff --git a/eval/buffer_test.go b/eval/buffer_test.go
index ea4c63c4..87bb1ecd 100644
--- a/eval/buffer_test.go
+++ b/eval/buffer_test.go
@@ -3,9 +3,8 @@ package eval
 import (
 	"testing"
 
-	"github.com/hashicorp/hcl/v2/hclsyntax"
-
 	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 )
 
 func TestMustBuffer(t *testing.T) {
diff --git a/eval/context_test.go b/eval/context_test.go
index 8f5dba9d..db38cd1a 100644
--- a/eval/context_test.go
+++ b/eval/context_test.go
@@ -237,9 +237,9 @@ func TestCouperVariables(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.name, func(subT *testing.T) {
 			bytes := []byte(tt.hcl)
-			hclBody, diags := parser.Load(bytes, "couper.hcl")
-			if diags.HasErrors() {
-				subT.Error(diags)
+			hclBody, err := parser.Load(bytes, "couper.hcl")
+			if err != nil {
+				subT.Error(err)
 				return
 			}
 
diff --git a/handler/endpoint_test.go b/handler/endpoint_test.go
index 8a86fc42..b1634406 100644
--- a/handler/endpoint_test.go
+++ b/handler/endpoint_test.go
@@ -13,8 +13,10 @@ import (
 
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/hclsimple"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 	logrustest "github.com/sirupsen/logrus/hooks/test"
 
+	hclbody "github.com/avenga/couper/config/body"
 	"github.com/avenga/couper/config/request"
 	"github.com/avenga/couper/errors"
 	"github.com/avenga/couper/eval"
@@ -83,12 +85,12 @@ func TestEndpoint_RoundTrip_Eval(t *testing.T) {
 			helper.Must(err)
 
 			backend := transport.NewBackend(
-				test.NewRemainContext("origin", "http://"+origin.Listener.Addr().String()),
+				hclbody.NewHCLSyntaxBodyWithStringAttr("origin", "http://"+origin.Listener.Addr().String()),
 				&transport.Config{NoProxyFromEnv: true}, nil, logger)
 
 			ep := handler.NewEndpoint(&handler.EndpointOptions{
 				ErrorTemplate: errors.DefaultJSON,
-				Context:       remain.Inline,
+				Context:       remain.Inline.(*hclsyntax.Body),
 				ReqBodyLimit:  1024,
 				Proxies: producer.Proxies{
 					&producer.Proxy{Name: "default", RoundTrip: backend},
@@ -212,7 +214,7 @@ func TestEndpoint_RoundTripContext_Variables_json_body(t *testing.T) {
 
 				ep := handler.NewEndpoint(&handler.EndpointOptions{
 					ErrorTemplate: errors.DefaultJSON,
-					Context:       hcl.EmptyBody(),
+					Context:       &hclsyntax.Body{},
 					ReqBodyLimit:  1024,
 					Proxies: producer.Proxies{
 						&producer.Proxy{Name: "default", RoundTrip: backend},
@@ -327,7 +329,7 @@ func TestEndpoint_RoundTripContext_Null_Eval(t *testing.T) {
 			h := test.New(subT)
 
 			backend := transport.NewBackend(
-				test.NewRemainContext("origin", "http://"+origin.Listener.Addr().String()),
+				hclbody.NewHCLSyntaxBodyWithStringAttr("origin", "http://"+origin.Listener.Addr().String()),
 				&transport.Config{NoProxyFromEnv: true}, nil, logger)
 
 			bufOpts := eval.MustBuffer(helper.NewInlineContext(tc.remain))
@@ -420,13 +422,13 @@ func TestEndpoint_ServeHTTP_FaultyDefaultResponse(t *testing.T) {
 	defer origin.Close()
 
 	rt := transport.NewBackend(
-		test.NewRemainContext("origin", origin.URL), &transport.Config{},
+		hclbody.NewHCLSyntaxBodyWithStringAttr("origin", origin.URL), &transport.Config{},
 		&transport.BackendOptions{}, log.WithContext(context.Background()))
 
 	mockProducer := &mockProducerResult{rt}
 
 	ep := handler.NewEndpoint(&handler.EndpointOptions{
-		Context:       hcl.EmptyBody(),
+		Context:       &hclsyntax.Body{},
 		ErrorTemplate: errors.DefaultJSON,
 		Proxies:       &mockProducerResult{},
 		Requests:      mockProducer,
@@ -473,13 +475,13 @@ func TestEndpoint_ServeHTTP_Cancel(t *testing.T) {
 	ctx = context.WithValue(ctx, request.StartTime, time.Now())
 
 	rt := transport.NewBackend(
-		test.NewRemainContext("origin", slowOrigin.URL), &transport.Config{},
+		hclbody.NewHCLSyntaxBodyWithStringAttr("origin", slowOrigin.URL), &transport.Config{},
 		&transport.BackendOptions{}, log.WithContext(context.Background()))
 
 	mockProducer := &mockProducerResult{rt}
 
 	ep := handler.NewEndpoint(&handler.EndpointOptions{
-		Context:       hcl.EmptyBody(),
+		Context:       &hclsyntax.Body{},
 		ErrorTemplate: errors.DefaultJSON,
 		Proxies:       &mockProducerResult{},
 		Requests:      mockProducer,
diff --git a/handler/producer/request_test.go b/handler/producer/request_test.go
index b5498a42..e66c7bd3 100644
--- a/handler/producer/request_test.go
+++ b/handler/producer/request_test.go
@@ -7,11 +7,9 @@ import (
 	"strconv"
 	"testing"
 
-	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/zclconf/go-cty/cty"
 
-	hclbody "github.com/avenga/couper/config/body"
 	"github.com/avenga/couper/errors"
 	"github.com/avenga/couper/eval"
 	"github.com/avenga/couper/handler"
@@ -34,7 +32,7 @@ func Test_ProduceExpectedStatus(t *testing.T) {
 	logger, _ := test.NewLogger()
 	logEntry := logger.WithContext(context.Background())
 
-	backend := transport.NewBackend(hcl.EmptyBody(), &transport.Config{Origin: origin.URL}, nil, logEntry)
+	backend := transport.NewBackend(&hclsyntax.Body{}, &transport.Config{Origin: origin.URL}, nil, logEntry)
 
 	clientRequest, _ := http.NewRequest(http.MethodGet, "http://couper.local", nil)
 
@@ -48,18 +46,18 @@ func Test_ProduceExpectedStatus(t *testing.T) {
 
 	tests := []struct {
 		name          string
-		attr          *hcl.Attribute
+		attr          *hclsyntax.Attribute
 		reflectStatus int // send via header, reflected by origin as http status-code
 		expectedErr   error
 	}{
 		{"/wo status", nil, http.StatusNoContent, nil},
-		{"/w status /w unexpected response", &hcl.Attribute{
+		{"/w status /w unexpected response", &hclsyntax.Attribute{
 			Name: "expected_status",
 			Expr: &hclsyntax.LiteralValueExpr{Val: toListVal(200, 304)}},
 			http.StatusNotAcceptable,
 			errors.UnexpectedStatus,
 		},
-		{"/w status /w expected response", &hcl.Attribute{
+		{"/w status /w expected response", &hclsyntax.Attribute{
 			Name: "expected_status",
 			Expr: &hclsyntax.LiteralValueExpr{Val: toListVal(200, 304)}},
 			http.StatusNotModified,
@@ -68,7 +66,7 @@ func Test_ProduceExpectedStatus(t *testing.T) {
 	}
 
 	for _, tt := range tests {
-		content := &hcl.BodyContent{Attributes: map[string]*hcl.Attribute{
+		content := &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{
 			"url": {Name: "url", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal(origin.URL)}},
 			// Since request will not proxy our dynamic client-request header value, we will add a headers attr here.
 			// There is no validation, so this also applies to proxy (unused)
@@ -87,14 +85,14 @@ func Test_ProduceExpectedStatus(t *testing.T) {
 
 		requests := producer.Requests{&producer.Request{
 			Backend: backend,
-			Context: hclbody.New(content),
+			Context: content,
 			Name:    "request",
 		}}
 
 		proxies := producer.Proxies{&producer.Proxy{
-			Content:   hclbody.New(content),
+			Content:   content,
 			Name:      "proxy",
-			RoundTrip: handler.NewProxy(backend, hclbody.New(content), logEntry),
+			RoundTrip: handler.NewProxy(backend, content, logEntry),
 		}}
 
 		testNames := []string{"request", "proxy"}
diff --git a/handler/proxy_test.go b/handler/proxy_test.go
index 5927c3fa..e625bef8 100644
--- a/handler/proxy_test.go
+++ b/handler/proxy_test.go
@@ -6,9 +6,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/zclconf/go-cty/cty"
-
-	"github.com/hashicorp/hcl/v2"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
 	"github.com/avenga/couper/config"
 	"github.com/avenga/couper/config/body"
@@ -22,12 +20,10 @@ func TestProxy_BlacklistHeaderRemoval(t *testing.T) {
 	log, _ := test.NewLogger()
 	logEntry := log.WithContext(context.Background())
 	p := handler.NewProxy(
-		transport.NewBackend(body.New(&hcl.BodyContent{Attributes: map[string]*hcl.Attribute{
-			"origin": {Name: "origin", Expr: hcl.StaticExpr(cty.StringVal("https://1.2.3.4"), hcl.Range{})},
-		}}), &transport.Config{
+		transport.NewBackend(body.NewHCLSyntaxBodyWithStringAttr("origin", "https://1.2.3.4"), &transport.Config{
 			Origin: "https://1.2.3.4/",
 		}, nil, logEntry),
-		hcl.EmptyBody(),
+		&hclsyntax.Body{},
 		logEntry,
 	)
 
diff --git a/handler/transport/backend_test.go b/handler/transport/backend_test.go
index 6a8fa744..d40ccfea 100644
--- a/handler/transport/backend_test.go
+++ b/handler/transport/backend_test.go
@@ -13,9 +13,7 @@ import (
 	"time"
 
 	"github.com/google/go-cmp/cmp"
-	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/hclsyntax"
-	"github.com/hashicorp/hcl/v2/hcltest"
 	logrustest "github.com/sirupsen/logrus/hooks/test"
 	"github.com/zclconf/go-cty/cty"
 
@@ -39,25 +37,25 @@ func TestBackend_RoundTrip_Timings(t *testing.T) {
 	}))
 	defer origin.Close()
 
-	withTimingsFn := func(base hcl.Body, connect, ttfb, timeout string) hcl.Body {
-		content := &hcl.BodyContent{Attributes: map[string]*hcl.Attribute{
+	withTimingsFn := func(base *hclsyntax.Body, connect, ttfb, timeout string) *hclsyntax.Body {
+		content := &hclsyntax.Body{Attributes: hclsyntax.Attributes{
 			"connect_timeout": {Name: "connect_timeout", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal(connect)}},
 			"ttfb_timeout":    {Name: "ttfb_timeout", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal(ttfb)}},
 			"timeout":         {Name: "timeout", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal(timeout)}},
 		}}
-		return hclbody.MergeBodies(base, hclbody.New(content))
+		return hclbody.MergeBodies(base, content, true)
 	}
 
 	tests := []struct {
 		name        string
-		context     hcl.Body
+		context     *hclsyntax.Body
 		req         *http.Request
 		expectedErr string
 	}{
-		{"with zero timings", test.NewRemainContext("origin", origin.URL), httptest.NewRequest(http.MethodGet, "http://1.2.3.4/", nil), ""},
-		{"with overall timeout", withTimingsFn(test.NewRemainContext("origin", origin.URL), "1m", "30s", "500ms"), httptest.NewRequest(http.MethodHead, "http://1.2.3.5/", nil), "deadline exceeded"},
-		{"with connect timeout", withTimingsFn(test.NewRemainContext("origin", "http://blackhole.webpagetest.org"), "750ms", "500ms", "1m"), httptest.NewRequest(http.MethodGet, "http://1.2.3.6/", nil), "i/o timeout"},
-		{"with ttfb timeout", withTimingsFn(test.NewRemainContext("origin", origin.URL), "10s", "1s", "1m"), httptest.NewRequest(http.MethodHead, "http://1.2.3.7/", nil), "timeout awaiting response headers"},
+		{"with zero timings", hclbody.NewHCLSyntaxBodyWithStringAttr("origin", origin.URL), httptest.NewRequest(http.MethodGet, "http://1.2.3.4/", nil), ""},
+		{"with overall timeout", withTimingsFn(hclbody.NewHCLSyntaxBodyWithStringAttr("origin", origin.URL), "1m", "30s", "500ms"), httptest.NewRequest(http.MethodHead, "http://1.2.3.5/", nil), "deadline exceeded"},
+		{"with connect timeout", withTimingsFn(hclbody.NewHCLSyntaxBodyWithStringAttr("origin", "http://blackhole.webpagetest.org"), "750ms", "500ms", "1m"), httptest.NewRequest(http.MethodGet, "http://1.2.3.6/", nil), "i/o timeout"},
+		{"with ttfb timeout", withTimingsFn(hclbody.NewHCLSyntaxBodyWithStringAttr("origin", origin.URL), "10s", "1s", "1m"), httptest.NewRequest(http.MethodHead, "http://1.2.3.7/", nil), "timeout awaiting response headers"},
 	}
 
 	logger, hook := logrustest.NewNullLogger()
@@ -99,12 +97,7 @@ func TestBackend_Compression_Disabled(t *testing.T) {
 	logger, _ := logrustest.NewNullLogger()
 	log := logger.WithContext(context.Background())
 
-	u := seetie.GoToValue(origin.URL)
-	hclBody := hcltest.MockBody(&hcl.BodyContent{
-		Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
-			"origin": hcltest.MockExprLiteral(u),
-		}),
-	})
+	hclBody := hclbody.NewHCLSyntaxBodyWithStringAttr("origin", origin.URL)
 	backend := transport.NewBackend(hclBody, &transport.Config{}, nil, log)
 
 	req := httptest.NewRequest(http.MethodOptions, "http://1.2.3.4/", nil)
@@ -139,12 +132,7 @@ func TestBackend_Compression_ModifyAcceptEncoding(t *testing.T) {
 	logger, _ := logrustest.NewNullLogger()
 	log := logger.WithContext(context.Background())
 
-	u := seetie.GoToValue(origin.URL)
-	hclBody := hcltest.MockBody(&hcl.BodyContent{
-		Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
-			"origin": hcltest.MockExprLiteral(u),
-		}),
-	})
+	hclBody := hclbody.NewHCLSyntaxBodyWithStringAttr("origin", origin.URL)
 
 	backend := transport.NewBackend(hclBody, &transport.Config{
 		Origin: origin.URL,
@@ -371,7 +359,8 @@ func TestBackend_director(t *testing.T) {
 			hclContext := helper.NewInlineContext(tt.inlineCtx)
 
 			backend := transport.NewBackend(hclbody.MergeBodies(hclContext,
-				hclbody.New(hclbody.NewContentWithAttrName("timeout", "1s")),
+				hclbody.NewHCLSyntaxBodyWithStringAttr("timeout", "1s"),
+				true,
 			), &transport.Config{}, nil, nullLog)
 
 			req := httptest.NewRequest(http.MethodGet, "https://example.com"+tt.path, nil)
diff --git a/handler/validation/openapi_test.go b/handler/validation/openapi_test.go
index e05cc608..1f11acf8 100644
--- a/handler/validation/openapi_test.go
+++ b/handler/validation/openapi_test.go
@@ -9,8 +9,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/hashicorp/hcl/v2"
-	"github.com/hashicorp/hcl/v2/hcltest"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/zclconf/go-cty/cty"
 
 	"github.com/avenga/couper/config"
@@ -44,21 +43,14 @@ func TestOpenAPIValidator_ValidateRequest(t *testing.T) {
 
 	log, hook := test.NewLogger()
 	logger := log.WithContext(context.Background())
-	beConf := &config.Backend{
-		Remain: body.New(&hcl.BodyContent{Attributes: hcl.Attributes{
-			"origin": &hcl.Attribute{
-				Name: "origin",
-				Expr: hcltest.MockExprLiteral(cty.StringVal(origin.URL)),
-			},
-		}}),
-		OpenAPI: &config.OpenAPI{
-			File: filepath.Join("testdata/backend_01_openapi.yaml"),
-		},
+	backendBody := body.NewHCLSyntaxBodyWithStringAttr("origin", origin.URL)
+	oa := &config.OpenAPI{
+		File: filepath.Join("testdata/backend_01_openapi.yaml"),
 	}
-	openAPI, err := validation.NewOpenAPIOptions(beConf.OpenAPI)
+	openAPI, err := validation.NewOpenAPIOptions(oa)
 	helper.Must(err)
 
-	backend := transport.NewBackend(beConf.Remain, &transport.Config{}, &transport.BackendOptions{
+	backend := transport.NewBackend(backendBody, &transport.Config{}, &transport.BackendOptions{
 		OpenAPI: openAPI,
 	}, logger)
 
@@ -124,21 +116,14 @@ func TestOpenAPIValidator_RelativeServerURL(t *testing.T) {
 
 	log, hook := test.NewLogger()
 	logger := log.WithContext(context.Background())
-	beConf := &config.Backend{
-		Remain: body.New(&hcl.BodyContent{Attributes: hcl.Attributes{
-			"origin": &hcl.Attribute{
-				Name: "origin",
-				Expr: hcltest.MockExprLiteral(cty.StringVal("https://httpbin.org")),
-			},
-		}}),
-		OpenAPI: &config.OpenAPI{
-			File: filepath.Join("testdata/backend_02_openapi.yaml"),
-		},
+	backendBody := body.NewHCLSyntaxBodyWithStringAttr("origin", "https://httpbin.org")
+	oa := &config.OpenAPI{
+		File: filepath.Join("testdata/backend_02_openapi.yaml"),
 	}
-	openAPI, err := validation.NewOpenAPIOptions(beConf.OpenAPI)
+	openAPI, err := validation.NewOpenAPIOptions(oa)
 	helper.Must(err)
 
-	backend := transport.NewBackend(beConf.Remain, &transport.Config{}, &transport.BackendOptions{
+	backend := transport.NewBackend(backendBody, &transport.Config{}, &transport.BackendOptions{
 		OpenAPI: openAPI,
 	}, logger)
 
@@ -183,28 +168,26 @@ func TestOpenAPIValidator_TemplateVariables(t *testing.T) {
 		{name: "relative url", origin: origin.Addr()},
 	} {
 		t.Run(tc.name, func(subT *testing.T) {
-			beConf := &config.Backend{
-				Remain: body.New(&hcl.BodyContent{Attributes: hcl.Attributes{
-					"origin": &hcl.Attribute{
-						Name: "origin",
-						Expr: hcltest.MockExprLiteral(cty.StringVal(tc.origin)),
-					},
-					"hostname": &hcl.Attribute{
-						Name: "hostname",
-						Expr: hcltest.MockExprLiteral(cty.StringVal(tc.hostname)),
-					},
-					"proxy": &hcl.Attribute{
-						Name: "proxy",
-						Expr: hcltest.MockExprLiteral(cty.StringVal(origin.Addr())),
-					},
-					"path": &hcl.Attribute{
-						Name: "path",
-						Expr: hcltest.MockExprLiteral(cty.StringVal("/anything")),
-					},
-				}}),
-			}
+			backendBody := &hclsyntax.Body{Attributes: hclsyntax.Attributes{
+				"origin": &hclsyntax.Attribute{
+					Name: "origin",
+					Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal(tc.origin)},
+				},
+				"hostname": &hclsyntax.Attribute{
+					Name: "hostname",
+					Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal(tc.hostname)},
+				},
+				"proxy": &hclsyntax.Attribute{
+					Name: "proxy",
+					Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal(origin.Addr())},
+				},
+				"path": &hclsyntax.Attribute{
+					Name: "path",
+					Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("/anything")},
+				},
+			}}
 
-			backend := transport.NewBackend(beConf.Remain, &transport.Config{}, &transport.BackendOptions{
+			backend := transport.NewBackend(backendBody, &transport.Config{}, &transport.BackendOptions{
 				OpenAPI: openAPI,
 			}, logger)
 
@@ -254,20 +237,20 @@ func TestOpenAPIValidator_NonCanonicalServerURL(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(subT *testing.T) {
-			Remain := body.New(&hcl.BodyContent{Attributes: hcl.Attributes{
-				"origin": &hcl.Attribute{
+			Remain := &hclsyntax.Body{Attributes: hclsyntax.Attributes{
+				"origin": &hclsyntax.Attribute{
 					Name: "origin",
-					Expr: hcltest.MockExprLiteral(cty.StringVal(tt.url)),
+					Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal(tt.url)},
 				},
-				"path": &hcl.Attribute{
+				"path": &hclsyntax.Attribute{
 					Name: "path",
-					Expr: hcltest.MockExprLiteral(cty.StringVal("/anything")),
+					Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("/anything")},
 				},
-				"proxy": &hcl.Attribute{
+				"proxy": &hclsyntax.Attribute{
 					Name: "proxy",
-					Expr: hcltest.MockExprLiteral(cty.StringVal(origin.Addr())),
+					Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal(origin.Addr())},
 				},
-			}})
+			}}
 
 			backend := transport.NewBackend(Remain, &transport.Config{}, &transport.BackendOptions{
 				OpenAPI: openAPI,
diff --git a/internal/test/hcl.go b/internal/test/hcl.go
deleted file mode 100644
index c98a817e..00000000
--- a/internal/test/hcl.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package test
-
-import (
-	"github.com/hashicorp/hcl/v2"
-	"github.com/hashicorp/hcl/v2/hcltest"
-	"github.com/zclconf/go-cty/cty"
-
-	"github.com/avenga/couper/config/body"
-)
-
-func NewRemainContext(name, value string) hcl.Body {
-	expr := hcltest.MockExprLiteral(cty.StringVal(value))
-	return body.MergeBodies(hcltest.MockBody(&hcl.BodyContent{Attributes: map[string]*hcl.Attribute{
-		name: {Name: name, Expr: expr},
-	}}))
-}
diff --git a/internal/test/helper_proxy.go b/internal/test/helper_proxy.go
index 843e5652..cb270a26 100644
--- a/internal/test/helper_proxy.go
+++ b/internal/test/helper_proxy.go
@@ -3,17 +3,17 @@ package test
 import (
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/hclsimple"
+	"github.com/hashicorp/hcl/v2/hclsyntax"
 
-	"github.com/avenga/couper/config/body"
 	"github.com/avenga/couper/eval"
 )
 
-func (h *Helper) NewInlineContext(inlineHCL string) hcl.Body {
+func (h *Helper) NewInlineContext(inlineHCL string) *hclsyntax.Body {
 	type hclBody struct {
 		Inline hcl.Body `hcl:",remain"`
 	}
 
 	var remain hclBody
 	h.Must(hclsimple.Decode(h.tb.Name()+".hcl", []byte(inlineHCL), eval.NewDefaultContext().HCLContext(), &remain))
-	return body.MergeBodies(remain.Inline)
+	return remain.Inline.(*hclsyntax.Body)
 }
diff --git a/oauth2/oidc/config_test.go b/oauth2/oidc/config_test.go
index a642aab0..a08072ec 100644
--- a/oauth2/oidc/config_test.go
+++ b/oauth2/oidc/config_test.go
@@ -9,13 +9,11 @@ import (
 	"testing"
 	"time"
 
-	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/zclconf/go-cty/cty"
 
 	"github.com/avenga/couper/accesscontrol/jwk"
 	"github.com/avenga/couper/config"
-	hclbody "github.com/avenga/couper/config/body"
 	"github.com/avenga/couper/config/configload"
 	"github.com/avenga/couper/handler/transport"
 	"github.com/avenga/couper/internal/test"
@@ -57,13 +55,13 @@ func TestConfig_Synced(t *testing.T) {
 	oconf := &config.OIDC{
 		ConfigurationURL: origin.URL + "/.well-known/openid-configuration",
 		ConfigurationTTL: "100ms",
-		Remain: hclbody.New(&hcl.BodyContent{Attributes: map[string]*hcl.Attribute{
+		Remain: &hclsyntax.Body{Attributes: hclsyntax.Attributes{
 			"redirect_uri":   {Name: "redirect_uri", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("")}},
 			"verifier_value": {Name: "verifier_value", Expr: &hclsyntax.LiteralValueExpr{Val: cty.StringVal("")}},
-		}}),
+		}},
 	}
 	// configload internals here... TODO: integration test?
-	err := oconf.Prepare(func(attr string, val string, body config.Inline) (hcl.Body, error) {
+	err := oconf.Prepare(func(attr string, val string, body config.Body) (*hclsyntax.Body, error) {
 		return configload.PrepareBackend(nil, attr, val, body)
 	})
 	helper.Must(err)
diff --git a/server/multi_files_test.go b/server/multi_files_test.go
index 85923154..a5f46624 100644
--- a/server/multi_files_test.go
+++ b/server/multi_files_test.go
@@ -1,6 +1,7 @@
 package server_test
 
 import (
+	"fmt"
 	"io"
 	"net/http"
 	"path/filepath"
@@ -181,18 +182,30 @@ func TestMultiFiles_Definitions(t *testing.T) {
 func TestMultiFiles_MultipleBackends(t *testing.T) {
 	type testCase struct {
 		config string
+		blType string
 	}
 
 	for _, tc := range []testCase{
-		{"testdata/multi/backends/errors/ac.hcl"},
-		{"testdata/multi/backends/errors/ac_eh.hcl"},
-		{"testdata/multi/backends/errors/ep.hcl"},
-		{"testdata/multi/backends/errors/api_ep.hcl"},
+		{"testdata/multi/backends/errors/jwt.hcl", "jwt"},
+		{"testdata/multi/backends/errors/beta_oauth2.hcl", "beta_oauth2"},
+		{"testdata/multi/backends/errors/oidc.hcl", "oidc"},
+		{"testdata/multi/backends/errors/ac_eh.hcl", "proxy"},
+		{"testdata/multi/backends/errors/ep_proxy.hcl", "proxy"},
+		{"testdata/multi/backends/errors/ep_request.hcl", "request"},
+		{"testdata/multi/backends/errors/api_ep.hcl", "proxy"},
+		{"testdata/multi/backends/errors/defined_backend_oauth2.hcl", "oauth2"},
+		{"testdata/multi/backends/errors/defined_backend_token_request.hcl", "beta_token_request"},
+		{"testdata/multi/backends/errors/anonymous_backend_oauth2.hcl", "oauth2"},
+		{"testdata/multi/backends/errors/anonymous_backend_token_request.hcl", "beta_token_request"},
 	} {
 		t.Run(tc.config, func(st *testing.T) {
 			_, err := configload.LoadFile(filepath.Join(testWorkingDir, tc.config), "")
 
-			if !strings.Contains(err.Error(), "Multiple definitions of backend are not allowed.") {
+			if !strings.Contains(err.Error(), tc.config+":") {
+				st.Errorf("Missing config file path: %s", err.Error())
+			}
+
+			if !strings.HasSuffix(err.Error(), fmt.Sprintf(": Multiple definitions of backend are not allowed in %s.; ", tc.blType)) {
 				st.Errorf("Unexpected error: %s", err.Error())
 			}
 		})
diff --git a/server/testdata/multi/backends/errors/ac_eh.hcl b/server/testdata/multi/backends/errors/ac_eh.hcl
index 9609e538..7a4d37c1 100644
--- a/server/testdata/multi/backends/errors/ac_eh.hcl
+++ b/server/testdata/multi/backends/errors/ac_eh.hcl
@@ -6,7 +6,7 @@ definitions {
           origin = "https:/example.com"
         }
         backend = "BE"
-	  }
+      }
     }
   }
 }
diff --git a/server/testdata/multi/backends/errors/anonymous_backend_oauth2.hcl b/server/testdata/multi/backends/errors/anonymous_backend_oauth2.hcl
new file mode 100644
index 00000000..e1f0601c
--- /dev/null
+++ b/server/testdata/multi/backends/errors/anonymous_backend_oauth2.hcl
@@ -0,0 +1,21 @@
+server {
+  endpoint "/" {
+    proxy {
+      backend {
+        origin = "https://example.com"
+        oauth2 {
+          grant_type = "client_credentials"
+          client_id = "cli"
+          client_secret = "cls"
+          token_endpoint = "https://as/token"
+          backend {
+            origin = "https://as"
+          }
+          backend = "BE"
+        }
+      }
+    }
+  }
+  backend "BE" {
+  }
+}
diff --git a/server/testdata/multi/backends/errors/anonymous_backend_token_request.hcl b/server/testdata/multi/backends/errors/anonymous_backend_token_request.hcl
new file mode 100644
index 00000000..b0b2ab58
--- /dev/null
+++ b/server/testdata/multi/backends/errors/anonymous_backend_token_request.hcl
@@ -0,0 +1,18 @@
+server {
+  endpoint "/" {
+    proxy {
+      backend {
+        origin = "https://example.com"
+        beta_token_request {
+          url = "https://as/token"
+          backend {
+            origin = "https://as"
+          }
+          backend = "BE"
+        }
+      }
+    }
+  }
+  backend "BE" {
+  }
+}
diff --git a/server/testdata/multi/backends/errors/api_ep.hcl b/server/testdata/multi/backends/errors/api_ep.hcl
index 64fc1654..db6fde76 100644
--- a/server/testdata/multi/backends/errors/api_ep.hcl
+++ b/server/testdata/multi/backends/errors/api_ep.hcl
@@ -3,10 +3,10 @@ server {
     endpoint "/" {
       proxy {
         backend {
-          origin = "https:/example.com"
+          origin = "https://example.com"
         }
         backend = "BE"
-  	  }
+      }
     }
   }
 }
diff --git a/server/testdata/multi/backends/errors/beta_oauth2.hcl b/server/testdata/multi/backends/errors/beta_oauth2.hcl
new file mode 100644
index 00000000..98214713
--- /dev/null
+++ b/server/testdata/multi/backends/errors/beta_oauth2.hcl
@@ -0,0 +1,8 @@
+definitions {
+  beta_oauth2 "oa" {
+    backend {
+      origin = "https://example.com"
+    }
+    backend = "BE"
+  }
+}
diff --git a/server/testdata/multi/backends/errors/defined_backend_oauth2.hcl b/server/testdata/multi/backends/errors/defined_backend_oauth2.hcl
new file mode 100644
index 00000000..1341f695
--- /dev/null
+++ b/server/testdata/multi/backends/errors/defined_backend_oauth2.hcl
@@ -0,0 +1,17 @@
+server {}
+definitions {
+  backend "b" {
+    oauth2 {
+      grant_type = "client_credentials"
+      client_id = "cli"
+      client_secret = "cls"
+      token_endpoint = "https://as/token"
+      backend {
+        origin = "https://as"
+      }
+      backend = "BE"
+    }
+  }
+  backend "BE" {
+  }
+}
diff --git a/server/testdata/multi/backends/errors/defined_backend_token_request.hcl b/server/testdata/multi/backends/errors/defined_backend_token_request.hcl
new file mode 100644
index 00000000..28879170
--- /dev/null
+++ b/server/testdata/multi/backends/errors/defined_backend_token_request.hcl
@@ -0,0 +1,14 @@
+server {}
+definitions {
+  backend "b" {
+    beta_token_request {
+      url = "https://as/token"
+      backend {
+        origin = "https://as"
+      }
+      backend = "BE"
+    }
+  }
+  backend "BE" {
+  }
+}
diff --git a/server/testdata/multi/backends/errors/ep.hcl b/server/testdata/multi/backends/errors/ep_proxy.hcl
similarity index 95%
rename from server/testdata/multi/backends/errors/ep.hcl
rename to server/testdata/multi/backends/errors/ep_proxy.hcl
index 89dc31ab..23972d8c 100644
--- a/server/testdata/multi/backends/errors/ep.hcl
+++ b/server/testdata/multi/backends/errors/ep_proxy.hcl
@@ -5,6 +5,6 @@ server {
         origin = "https:/example.com"
       }
       backend = "BE"
-	}
+    }
   }
 }
diff --git a/server/testdata/multi/backends/errors/ep_request.hcl b/server/testdata/multi/backends/errors/ep_request.hcl
new file mode 100644
index 00000000..1dbc2531
--- /dev/null
+++ b/server/testdata/multi/backends/errors/ep_request.hcl
@@ -0,0 +1,10 @@
+server {
+  endpoint "/" {
+    request {
+      backend {
+        origin = "https:/example.com"
+      }
+      backend = "BE"
+    }
+  }
+}
diff --git a/server/testdata/multi/backends/errors/ac.hcl b/server/testdata/multi/backends/errors/jwt.hcl
similarity index 66%
rename from server/testdata/multi/backends/errors/ac.hcl
rename to server/testdata/multi/backends/errors/jwt.hcl
index 5d4cb371..6fcd47d1 100644
--- a/server/testdata/multi/backends/errors/ac.hcl
+++ b/server/testdata/multi/backends/errors/jwt.hcl
@@ -1,7 +1,7 @@
 definitions {
   jwt "JWT" {
     backend {
-      origin = "https:/example.com"
+      origin = "https://example.com"
     }
     backend = "BE"
   }
diff --git a/server/testdata/multi/backends/errors/oidc.hcl b/server/testdata/multi/backends/errors/oidc.hcl
new file mode 100644
index 00000000..87e84929
--- /dev/null
+++ b/server/testdata/multi/backends/errors/oidc.hcl
@@ -0,0 +1,8 @@
+definitions {
+  oidc "o" {
+    backend {
+      origin = "https://example.com"
+    }
+    backend = "BE"
+  }
+}